forked from cryptonotefoundation/cryptonote
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Multisignature API, Low level and High level APIs
Antonio Juarez
committed
Apr 6, 2015
1 parent
257a2bf
commit 1743402
Showing
277 changed files
with
30,175 additions
and
9,760 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
namespace CryptoNote { | ||
|
||
template <typename T> | ||
class IObservable { | ||
public: | ||
virtual void addObserver(T* observer) = 0; | ||
virtual void removeObserver(T* observer) = 0; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <sstream> | ||
|
||
namespace CryptoNote { | ||
|
||
class IStreamSerializable { | ||
public: | ||
virtual void save(std::ostream& os) = 0; | ||
virtual void load(std::istream& in) = 0; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <array> | ||
#include <cstdint> | ||
#include <vector> | ||
|
||
namespace CryptoNote { | ||
|
||
typedef std::array<uint8_t, 32> PublicKey; | ||
typedef std::array<uint8_t, 32> SecretKey; | ||
typedef std::array<uint8_t, 32> KeyImage; | ||
typedef std::array<uint8_t, 32> Hash; | ||
typedef std::vector<uint8_t> Blob; | ||
|
||
struct AccountAddress { | ||
PublicKey spendPublicKey; | ||
PublicKey viewPublicKey; | ||
}; | ||
|
||
struct AccountKeys { | ||
AccountAddress address; | ||
SecretKey spendSecretKey; | ||
SecretKey viewSecretKey; | ||
}; | ||
|
||
struct KeyPair { | ||
PublicKey publicKey; | ||
SecretKey secretKey; | ||
}; | ||
|
||
namespace TransactionTypes { | ||
|
||
enum class InputType : uint8_t { Invalid, Key, Multisignature, Generating }; | ||
enum class OutputType : uint8_t { Invalid, Key, Multisignature }; | ||
|
||
struct InputKey { | ||
uint64_t amount; | ||
std::vector<uint64_t> keyOffsets; | ||
KeyImage keyImage; // double spending protection | ||
}; | ||
|
||
struct InputMultisignature { | ||
uint64_t amount; | ||
uint32_t signatures; | ||
uint64_t outputIndex; | ||
}; | ||
|
||
struct OutputKey { | ||
uint64_t amount; | ||
PublicKey key; | ||
}; | ||
|
||
struct OutputMultisignature { | ||
uint64_t amount; | ||
std::vector<PublicKey> keys; | ||
uint32_t requiredSignatures; | ||
}; | ||
|
||
struct GlobalOutput { | ||
PublicKey targetKey; | ||
uint64_t outputIndex; | ||
}; | ||
|
||
typedef std::vector<GlobalOutput> GlobalOutputsContainer; | ||
|
||
struct OutputKeyInfo { | ||
PublicKey transactionPublicKey; | ||
size_t transactionIndex; | ||
size_t outputInTransaction; | ||
}; | ||
|
||
struct InputKeyInfo { | ||
uint64_t amount; | ||
GlobalOutputsContainer outputs; | ||
OutputKeyInfo realOutput; | ||
}; | ||
|
||
} | ||
|
||
// | ||
// ITransactionReader | ||
// | ||
class ITransactionReader { | ||
public: | ||
virtual ~ITransactionReader() { } | ||
|
||
virtual Hash getTransactionHash() const = 0; | ||
virtual Hash getTransactionPrefixHash() const = 0; | ||
virtual PublicKey getTransactionPublicKey() const = 0; | ||
virtual uint64_t getUnlockTime() const = 0; | ||
|
||
// extra | ||
virtual bool getPaymentId(Hash& paymentId) const = 0; | ||
virtual bool getExtraNonce(std::string& nonce) const = 0; | ||
|
||
// inputs | ||
virtual size_t getInputCount() const = 0; | ||
virtual uint64_t getInputTotalAmount() const = 0; | ||
virtual TransactionTypes::InputType getInputType(size_t index) const = 0; | ||
virtual void getInput(size_t index, TransactionTypes::InputKey& input) const = 0; | ||
virtual void getInput(size_t index, TransactionTypes::InputMultisignature& input) const = 0; | ||
|
||
// outputs | ||
virtual size_t getOutputCount() const = 0; | ||
virtual uint64_t getOutputTotalAmount() const = 0; | ||
virtual TransactionTypes::OutputType getOutputType(size_t index) const = 0; | ||
virtual void getOutput(size_t index, TransactionTypes::OutputKey& output) const = 0; | ||
virtual void getOutput(size_t index, TransactionTypes::OutputMultisignature& output) const = 0; | ||
|
||
// signatures | ||
virtual size_t getRequiredSignaturesCount(size_t inputIndex) const = 0; | ||
virtual bool findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector<uint32_t>& outs, uint64_t& outputAmount) const = 0; | ||
|
||
// various checks | ||
virtual bool validateInputs() const = 0; | ||
virtual bool validateOutputs() const = 0; | ||
virtual bool validateSignatures() const = 0; | ||
|
||
// serialized transaction | ||
virtual Blob getTransactionData() const = 0; | ||
}; | ||
|
||
// | ||
// ITransactionWriter | ||
// | ||
class ITransactionWriter { | ||
public: | ||
|
||
virtual ~ITransactionWriter() { } | ||
|
||
// transaction parameters | ||
virtual void setUnlockTime(uint64_t unlockTime) = 0; | ||
|
||
// extra | ||
virtual void setPaymentId(const Hash& paymentId) = 0; | ||
virtual void setExtraNonce(const std::string& nonce) = 0; | ||
|
||
// Inputs/Outputs | ||
virtual size_t addInput(const TransactionTypes::InputKey& input) = 0; | ||
virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) = 0; | ||
virtual size_t addInput(const TransactionTypes::InputMultisignature& input) = 0; | ||
|
||
virtual size_t addOutput(uint64_t amount, const AccountAddress& to) = 0; | ||
virtual size_t addOutput(uint64_t amount, const std::vector<AccountAddress>& to, uint32_t requiredSignatures) = 0; | ||
|
||
// transaction info | ||
virtual bool getTransactionSecretKey(SecretKey& key) const = 0; | ||
virtual void setTransactionSecretKey(const SecretKey& key) = 0; | ||
|
||
// signing | ||
virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) = 0; | ||
virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) = 0; | ||
}; | ||
|
||
class ITransaction : | ||
public ITransactionReader, | ||
public ITransactionWriter { | ||
public: | ||
virtual ~ITransaction() { } | ||
|
||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <limits> | ||
#include <vector> | ||
#include "crypto/hash.h" | ||
#include "ITransaction.h" | ||
#include "IObservable.h" | ||
#include "IStreamSerializable.h" | ||
|
||
namespace CryptoNote { | ||
|
||
const uint64_t UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX = std::numeric_limits<uint64_t>::max(); | ||
|
||
struct TransactionInformation { | ||
// transaction info | ||
Hash transactionHash; | ||
PublicKey publicKey; | ||
uint64_t blockHeight; | ||
uint64_t timestamp; | ||
uint64_t unlockTime; | ||
uint64_t totalAmountIn; | ||
uint64_t totalAmountOut; | ||
std::vector<uint8_t> extra; | ||
Hash paymentId; | ||
}; | ||
|
||
|
||
struct TransactionOutputInformation { | ||
// output info | ||
TransactionTypes::OutputType type; | ||
uint64_t amount; | ||
uint64_t globalOutputIndex; | ||
uint32_t outputInTransaction; | ||
|
||
// transaction info | ||
Hash transactionHash; | ||
PublicKey transactionPublicKey; | ||
|
||
union { | ||
PublicKey outputKey; // Type: Key | ||
uint32_t requiredSignatures; // Type: Multisignature | ||
}; | ||
}; | ||
|
||
struct TransactionSpentOutputInformation: public TransactionOutputInformation { | ||
uint64_t spendingBlockHeight; | ||
uint64_t timestamp; | ||
Hash spendingTransactionHash; | ||
KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key | ||
uint32_t inputInTransaction; | ||
}; | ||
|
||
class ITransfersContainer : public IStreamSerializable { | ||
public: | ||
enum Flags : uint32_t { | ||
// state | ||
IncludeStateUnlocked = 0x01, | ||
IncludeStateLocked = 0x02, | ||
IncludeStateSoftLocked = 0x04, | ||
// output type | ||
IncludeTypeKey = 0x100, | ||
IncludeTypeMultisignature = 0x200, | ||
// combinations | ||
IncludeStateAll = 0xff, | ||
IncludeTypeAll = 0xff00, | ||
|
||
IncludeKeyUnlocked = IncludeTypeKey | IncludeStateUnlocked, | ||
IncludeKeyNotUnlocked = IncludeTypeKey | IncludeStateLocked | IncludeStateSoftLocked, | ||
|
||
IncludeAllLocked = IncludeTypeAll | IncludeStateLocked | IncludeStateSoftLocked, | ||
IncludeAllUnlocked = IncludeTypeAll | IncludeStateUnlocked, | ||
IncludeAll = IncludeTypeAll | IncludeStateAll, | ||
|
||
IncludeDefault = IncludeKeyUnlocked | ||
}; | ||
|
||
virtual size_t transfersCount() = 0; | ||
virtual size_t transactionsCount() = 0; | ||
virtual uint64_t balance(uint32_t flags = IncludeDefault) = 0; | ||
virtual void getOutputs(std::vector<TransactionOutputInformation>& transfers, uint32_t flags = IncludeDefault) = 0; | ||
virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) = 0; | ||
virtual std::vector<TransactionOutputInformation> getTransactionOutputs(const Hash& transactionHash, uint32_t flags = IncludeDefault) = 0; | ||
virtual void getUnconfirmedTransactions(std::vector<crypto::hash>& transactions) = 0; | ||
virtual std::vector<TransactionSpentOutputInformation> getSpentOutputs() = 0; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <system_error> | ||
|
||
#include "ITransaction.h" | ||
#include "ITransfersContainer.h" | ||
#include "IStreamSerializable.h" | ||
|
||
namespace CryptoNote { | ||
|
||
struct SynchronizationStart { | ||
uint64_t timestamp; | ||
uint64_t height; | ||
}; | ||
|
||
struct AccountSubscription { | ||
AccountKeys keys; | ||
SynchronizationStart syncStart; | ||
size_t transactionSpendableAge; | ||
}; | ||
|
||
class ITransfersSubscription; | ||
|
||
class ITransfersObserver { | ||
public: | ||
virtual void onError(ITransfersSubscription* object, | ||
uint64_t height, std::error_code ec) {} | ||
|
||
virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) {} | ||
|
||
/** | ||
* \note The sender must guarantee that onTransactionDeleted() is called only after onTransactionUpdated() is called | ||
* for the same \a transactionHash. | ||
*/ | ||
virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { } | ||
}; | ||
|
||
class ITransfersSubscription : public IObservable < ITransfersObserver > { | ||
public: | ||
virtual ~ITransfersSubscription() {} | ||
|
||
virtual AccountAddress getAddress() = 0; | ||
virtual ITransfersContainer& getContainer() = 0; | ||
}; | ||
|
||
class ITransfersSynchronizer : public IStreamSerializable { | ||
public: | ||
virtual ~ITransfersSynchronizer() {} | ||
|
||
virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) = 0; | ||
virtual bool removeSubscription(const AccountAddress& acc) = 0; | ||
virtual void getSubscriptions(std::vector<AccountAddress>& subscriptions) = 0; | ||
// returns nullptr if address is not found | ||
virtual ITransfersSubscription* getSubscription(const AccountAddress& acc) = 0; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
0
src/System/HttpParser.cpp → src/HTTP/HttpParser.cpp
100644 → 100755
File renamed without changes.
0
src/System/HttpParser.h → src/HTTP/HttpParser.h
100644 → 100755
File renamed without changes.
0
src/System/HttpRequest.cpp → src/HTTP/HttpRequest.cpp
100644 → 100755
File renamed without changes.
0
src/System/HttpRequest.h → src/HTTP/HttpRequest.h
100644 → 100755
File renamed without changes.
0
src/System/HttpResponse.cpp → src/HTTP/HttpResponse.cpp
100644 → 100755
File renamed without changes.
0
src/System/HttpResponse.h → src/HTTP/HttpResponse.h
100644 → 100755
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#include "Dispatcher.h" | ||
#include <iostream> | ||
#include <ucontext.h> | ||
#include <unistd.h> | ||
#include <sys/types.h> | ||
#include <sys/epoll.h> | ||
#include <assert.h> | ||
#include <sys/time.h> | ||
#include <errno.h> | ||
#include <stdexcept> | ||
|
||
using namespace System; | ||
|
||
void Dispatcher::contextProcedureStatic(void *context) { | ||
reinterpret_cast<Dispatcher*>(context)->contextProcedure(); | ||
} | ||
|
||
Dispatcher::Dispatcher() { | ||
epoll = ::epoll_create1(0); | ||
if (epoll == -1) { | ||
std::cerr << "kqueue() fail errno=" << errno << std::endl; | ||
} else { | ||
currentContext = new ucontext_t; | ||
if (getcontext(reinterpret_cast<ucontext_t*>(currentContext)) == -1) { | ||
std::cerr << "getcontext() fail errno=" << errno << std::endl; | ||
} else { | ||
contextCount = 0; | ||
return; | ||
} | ||
} | ||
throw std::runtime_error("Dispatcher::Dispatcher"); | ||
} | ||
|
||
Dispatcher::~Dispatcher() { | ||
assert(resumingContexts.empty()); | ||
assert(reusableContexts.size() == contextCount); | ||
assert(spawningProcedures.empty()); | ||
assert(reusableContexts.size() == allocatedStacks.size()); | ||
while (!reusableContexts.empty()) { | ||
delete[] allocatedStacks.top(); | ||
allocatedStacks.pop(); | ||
delete static_cast<ucontext_t*>(reusableContexts.top()); | ||
reusableContexts.pop(); | ||
} | ||
|
||
while (!timers.empty()) { | ||
timers.pop(); | ||
} | ||
|
||
if (-1 == close(epoll)) { | ||
std::cerr << "close() fail errno=" << errno << std::endl; | ||
} | ||
} | ||
|
||
void* Dispatcher::getCurrentContext() const { | ||
return currentContext; | ||
} | ||
|
||
int Dispatcher::getEpoll() const { | ||
return epoll; | ||
} | ||
|
||
void Dispatcher::pushContext(void* context) { | ||
resumingContexts.push(context); | ||
} | ||
|
||
void Dispatcher::spawn(std::function<void()>&& procedure) { | ||
ucontext_t *context; | ||
if (reusableContexts.empty()) { | ||
context = new ucontext_t; | ||
if (getcontext(context) == -1) { //makecontext precondition | ||
std::cerr << "getcontext() fail errno=" << errno << std::endl; | ||
throw std::runtime_error("Dispatcher::spawn()"); | ||
} | ||
auto stackPointer = new uint8_t[64 * 1024]; | ||
context->uc_stack.ss_sp = stackPointer; | ||
allocatedStacks.push(stackPointer); | ||
context->uc_stack.ss_size = 64 * 1024; | ||
makecontext(context, (void(*)())contextProcedureStatic, 1, reinterpret_cast<int*>(this)); | ||
++contextCount; | ||
} else { | ||
context = static_cast<ucontext_t*>(reusableContexts.top()); | ||
reusableContexts.pop(); | ||
} | ||
|
||
resumingContexts.push(context); | ||
spawningProcedures.emplace(std::move(procedure)); | ||
} | ||
|
||
void Dispatcher::clear() { | ||
//TODO | ||
} | ||
|
||
void Dispatcher::yield() { | ||
void* context; | ||
for (;;) { | ||
if (!resumingContexts.empty()) { | ||
context = resumingContexts.front(); | ||
resumingContexts.pop(); | ||
assert(context); | ||
break; | ||
} | ||
|
||
epoll_event event; | ||
int count = epoll_wait(epoll, &event, 1, -1); | ||
|
||
if (count == 1) { | ||
if ((event.events & EPOLLOUT) != 0) { | ||
context = static_cast<ContextExt *>(event.data.ptr)->writeContext; | ||
} else { | ||
context = static_cast<ContextExt *>(event.data.ptr)->context; | ||
} | ||
assert(context); | ||
break; | ||
} | ||
|
||
if (errno != EINTR) { | ||
std::cerr << "epoll_wait() failed, errno=" << errno << std::endl; | ||
throw std::runtime_error("Dispatcher::yield()"); | ||
} | ||
} | ||
|
||
if (context != currentContext) { | ||
ucontext_t* oldContext = static_cast<ucontext_t*>(currentContext); | ||
currentContext = context; | ||
if (-1 == swapcontext(oldContext, static_cast<ucontext_t *>(context))) { | ||
std::cerr << "swapcontext() failed, errno=" << errno << std::endl; | ||
throw std::runtime_error("Dispatcher::yield()"); | ||
} | ||
} | ||
} | ||
|
||
void Dispatcher::contextProcedure() { | ||
void* context = currentContext; | ||
for (;;) { | ||
assert(!spawningProcedures.empty()); | ||
std::function<void()> procedure = std::move(spawningProcedures.front()); | ||
spawningProcedures.pop(); | ||
procedure(); | ||
reusableContexts.push(context); | ||
yield(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <functional> | ||
#include <queue> | ||
#include <stack> | ||
|
||
namespace System { | ||
|
||
class Dispatcher { | ||
public: | ||
Dispatcher(); | ||
Dispatcher(const Dispatcher&) = delete; | ||
~Dispatcher(); | ||
Dispatcher& operator=(const Dispatcher&) = delete; | ||
void spawn(std::function<void()>&& procedure); | ||
void yield(); | ||
void clear(); | ||
|
||
struct ContextExt { | ||
void *context; | ||
void *writeContext; //required workaround | ||
}; | ||
private: | ||
friend class Event; | ||
friend class DispatcherAccessor; | ||
friend class TcpConnection; | ||
friend class TcpConnector; | ||
friend class TcpListener; | ||
friend class Timer; | ||
int epoll; | ||
void* currentContext; | ||
std::size_t contextCount; | ||
std::queue<void*> resumingContexts; | ||
std::stack<void*> reusableContexts; | ||
std::stack<uint8_t *> allocatedStacks; | ||
std::queue<std::function<void()>> spawningProcedures; | ||
std::stack<int> timers; | ||
|
||
int getEpoll() const; | ||
void pushContext(void* context); | ||
void* getCurrentContext() const; | ||
|
||
void contextProcedure(); | ||
static void contextProcedureStatic(void* context); | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,297 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#include "TcpConnection.h" | ||
#include <iostream> | ||
#include <sys/epoll.h> | ||
#include <errno.h> | ||
#include <unistd.h> | ||
#include <assert.h> | ||
#include <stdexcept> | ||
#include <sys/socket.h> | ||
#include "Dispatcher.h" | ||
#include "InterruptedException.h" | ||
|
||
using namespace System; | ||
|
||
namespace { | ||
|
||
struct ConnectionContext : public Dispatcher::ContextExt { | ||
bool interrupted; | ||
}; | ||
|
||
} | ||
|
||
TcpConnection::TcpConnection() : dispatcher(nullptr) { | ||
} | ||
|
||
TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), context(nullptr) { | ||
epoll_event connectionEvent; | ||
connectionEvent.data.fd = connection; | ||
connectionEvent.events = 0; | ||
connectionEvent.data.ptr = nullptr; | ||
|
||
if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, socket, &connectionEvent) == -1) { | ||
std::cerr << errno << std::endl; | ||
throw std::runtime_error("epoll_ctl() fail"); | ||
} | ||
} | ||
|
||
TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) { | ||
if (other.dispatcher != nullptr) { | ||
connection = other.connection; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
} | ||
|
||
TcpConnection::~TcpConnection() { | ||
if (dispatcher != nullptr) { | ||
assert(context == nullptr); | ||
if (close(connection) == -1) { | ||
std::cerr << "close() failed, errno=" << errno << '.' << std::endl; | ||
} | ||
} | ||
} | ||
|
||
TcpConnection& TcpConnection::operator=(TcpConnection&& other) { | ||
if (dispatcher != nullptr) { | ||
assert(context == nullptr); | ||
if (close(connection) == -1) { | ||
std::cerr << "close() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpConnection::operator="); | ||
} | ||
} | ||
|
||
dispatcher = other.dispatcher; | ||
if (other.dispatcher != nullptr) { | ||
connection = other.connection; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
|
||
return *this; | ||
} | ||
|
||
void TcpConnection::start() { | ||
assert(dispatcher != nullptr); | ||
assert(stopped); | ||
stopped = false; | ||
} | ||
|
||
void TcpConnection::stop() { | ||
assert(dispatcher != nullptr); | ||
assert(!stopped); | ||
if (context != nullptr) { | ||
ConnectionContext *context2 = static_cast<ConnectionContext *>(context); | ||
if (!context2->interrupted) { | ||
|
||
epoll_event connectionEvent; | ||
connectionEvent.data.fd = connection; | ||
connectionEvent.events = 0; | ||
connectionEvent.data.ptr = nullptr; | ||
|
||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { | ||
std::cerr << errno << std::endl; | ||
throw std::runtime_error("epoll_ctl() fail"); | ||
} | ||
|
||
context2->interrupted = true; | ||
|
||
if (context2->context != nullptr) { | ||
dispatcher->pushContext(context2->context); | ||
} | ||
|
||
if (context2->writeContext != nullptr) { | ||
dispatcher->pushContext(context2->writeContext); | ||
} | ||
} | ||
} | ||
|
||
stopped = true; | ||
} | ||
|
||
size_t TcpConnection::read(uint8_t* data, size_t size) { | ||
assert(dispatcher != nullptr); | ||
assert(context == nullptr || static_cast<Dispatcher::ContextExt*>(context)->context == nullptr); | ||
if (stopped) { | ||
throw InterruptedException(); | ||
} | ||
|
||
ssize_t transferred = ::recv(connection, (void *)data, size, 0); | ||
if (transferred == -1) { | ||
if (errno != EAGAIN && errno != EWOULDBLOCK) { | ||
std::cerr << "recv failed, result=" << errno << '.' << std::endl; | ||
} else { | ||
epoll_event connectionEvent; | ||
connectionEvent.data.fd = connection; | ||
|
||
ConnectionContext context2; | ||
if (context == nullptr) { | ||
context2.writeContext = nullptr; | ||
context2.interrupted = false; | ||
context2.context = dispatcher->getCurrentContext(); | ||
context = &context2; | ||
connectionEvent.events = EPOLLIN | EPOLLONESHOT; | ||
} else { | ||
assert(static_cast<Dispatcher::ContextExt*>(context)->writeContext != nullptr); | ||
connectionEvent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT; | ||
} | ||
|
||
connectionEvent.data.ptr = context; | ||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
dispatcher->yield(); | ||
assert(dispatcher != nullptr); | ||
assert(context2.context == dispatcher->getCurrentContext()); | ||
if (static_cast<ConnectionContext*>(context)->interrupted) { | ||
context = nullptr; | ||
throw InterruptedException(); | ||
} | ||
|
||
assert(static_cast<Dispatcher::ContextExt*>(context)->context == context2.context); | ||
if (static_cast<Dispatcher::ContextExt*>(context)->writeContext != nullptr) { //write is presented, rearm | ||
static_cast<Dispatcher::ContextExt*>(context)->context = nullptr; | ||
|
||
epoll_event connectionEvent; | ||
connectionEvent.data.fd = connection; | ||
connectionEvent.events = EPOLLOUT | EPOLLONESHOT; | ||
connectionEvent.data.ptr = context; | ||
|
||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpConnection::read"); | ||
} | ||
} else { | ||
context = nullptr; | ||
} | ||
|
||
ssize_t transferred = ::recv(connection, (void *)data, size, 0); | ||
if (transferred == -1) { | ||
std::cerr << "recv failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
if (transferred == 0) { | ||
std::cerr << "recv return after yield with 0 bytes" << std::endl; | ||
|
||
int retval = -1; | ||
socklen_t retValLen = sizeof(retval); | ||
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); | ||
if (s == -1) { | ||
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
std::cerr << "recv getsockopt retval = " << retval << std::endl; | ||
} | ||
} | ||
|
||
assert(transferred <= size); | ||
return transferred; | ||
} | ||
} | ||
} | ||
|
||
throw std::runtime_error("TcpConnection::read"); | ||
} | ||
|
||
assert(transferred <= size); | ||
return transferred; | ||
} | ||
|
||
void TcpConnection::write(const uint8_t* data, size_t size) { | ||
assert(dispatcher != nullptr); | ||
assert(context == nullptr || static_cast<Dispatcher::ContextExt*>(context)->writeContext == nullptr); | ||
if (stopped) { | ||
throw InterruptedException(); | ||
} | ||
|
||
if (size == 0) { | ||
if (shutdown(connection, SHUT_WR) == -1) { | ||
std::cerr << "shutdown failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpConnection::write"); | ||
} | ||
|
||
return; | ||
} | ||
|
||
ssize_t transferred = ::send(connection, (void *)data, size, 0); | ||
if (transferred == -1) { | ||
if (errno != EAGAIN && errno != EWOULDBLOCK) { | ||
std::cerr << "send failed, result=" << errno << '.' << std::endl; | ||
} else { | ||
epoll_event connectionEvent; | ||
connectionEvent.data.fd = connection; | ||
|
||
ConnectionContext context2; | ||
if (context == nullptr) { | ||
context2.context = nullptr; | ||
context2.interrupted = false; | ||
context2.writeContext = dispatcher->getCurrentContext(); | ||
context = &context2; | ||
connectionEvent.events = EPOLLOUT | EPOLLONESHOT; | ||
} else { | ||
assert(static_cast<Dispatcher::ContextExt*>(context)->context != nullptr); | ||
connectionEvent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT; | ||
} | ||
|
||
connectionEvent.data.ptr = context; | ||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
dispatcher->yield(); | ||
assert(dispatcher != nullptr); | ||
assert(context2.writeContext == dispatcher->getCurrentContext()); | ||
if (static_cast<ConnectionContext*>(context)->interrupted) { | ||
context = nullptr; | ||
throw InterruptedException(); | ||
} | ||
|
||
assert(static_cast<Dispatcher::ContextExt*>(context)->writeContext == context2.writeContext); | ||
if (static_cast<Dispatcher::ContextExt*>(context)->context != nullptr) { //read is presented, rearm | ||
static_cast<Dispatcher::ContextExt*>(context)->writeContext = nullptr; | ||
|
||
epoll_event connectionEvent; | ||
connectionEvent.data.fd = connection; | ||
connectionEvent.events = EPOLLIN | EPOLLONESHOT; | ||
connectionEvent.data.ptr = context; | ||
|
||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpConnection::write"); | ||
} | ||
} else { | ||
context = nullptr; | ||
} | ||
|
||
ssize_t transferred = ::send(connection, (void *)data, size, 0); | ||
if (transferred == -1) { | ||
std::cerr << "send failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
if (transferred == 0) { | ||
throw std::runtime_error("send transferred 0 bytes."); | ||
} | ||
|
||
assert(transferred == size); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
throw std::runtime_error("TcpConnection::write"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#include "TcpConnector.h" | ||
|
||
#include <stdexcept> | ||
#include <iostream> | ||
#include <random> | ||
#include <sstream> | ||
#include <cassert> | ||
|
||
#include <netdb.h> | ||
#include <fcntl.h> | ||
#include <unistd.h> | ||
#include <errno.h> | ||
#include <sys/epoll.h> | ||
#include <sys/socket.h> | ||
|
||
#include "Dispatcher.h" | ||
#include "TcpConnection.h" | ||
#include "InterruptedException.h" | ||
|
||
using namespace System; | ||
|
||
namespace { | ||
|
||
struct ConnectorContext : public Dispatcher::ContextExt { | ||
int connection; | ||
bool interrupted; | ||
}; | ||
|
||
} | ||
|
||
TcpConnector::TcpConnector() : dispatcher(nullptr) { | ||
} | ||
|
||
TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) { | ||
} | ||
|
||
TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { | ||
if (other.dispatcher != nullptr) { | ||
address = other.address; | ||
port = other.port; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
} | ||
|
||
TcpConnector::~TcpConnector() { | ||
} | ||
|
||
TcpConnector& TcpConnector::operator=(TcpConnector&& other) { | ||
dispatcher = other.dispatcher; | ||
if (other.dispatcher != nullptr) { | ||
address = other.address; | ||
port = other.port; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
|
||
return *this; | ||
} | ||
|
||
void TcpConnector::start() { | ||
assert(dispatcher != nullptr); | ||
assert(stopped); | ||
stopped = false; | ||
} | ||
|
||
TcpConnection TcpConnector::connect() { | ||
assert(dispatcher != nullptr); | ||
assert(context == nullptr); | ||
if (stopped) { | ||
throw InterruptedException(); | ||
} | ||
|
||
std::ostringstream portStream; | ||
portStream << port; | ||
addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL }; | ||
addrinfo *addressInfos; | ||
int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos); | ||
if (result == -1) { | ||
std::cerr << "getaddrinfo failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
std::size_t count = 0; | ||
for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { | ||
++count; | ||
} | ||
|
||
std::random_device randomDevice; | ||
std::mt19937 generator(randomDevice()); | ||
std::uniform_int_distribution<std::size_t> distribution(0, count - 1); | ||
std::size_t index = distribution(generator); | ||
addrinfo* addressInfo = addressInfos; | ||
for (std::size_t i = 0; i < index; ++i) { | ||
addressInfo = addressInfo->ai_next; | ||
} | ||
|
||
sockaddr_in addressData = *reinterpret_cast<sockaddr_in*>(addressInfo->ai_addr); | ||
freeaddrinfo(addressInfo); | ||
int connection = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
if (connection == -1) { | ||
std::cerr << "socket failed, errno=" << errno << '.' << std::endl; | ||
} 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<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) { | ||
std::cerr << "bind failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
int flags = fcntl(connection, F_GETFL, 0); | ||
if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { | ||
std::cerr << "fcntl() failed errno=" << errno << std::endl; | ||
} else { | ||
int result = ::connect(connection, reinterpret_cast<sockaddr *>(&addressData), sizeof addressData); | ||
if (result == -1) { | ||
if (errno == EINPROGRESS) { | ||
ConnectorContext context2; | ||
context2.writeContext = dispatcher->getCurrentContext(); | ||
context2.context = nullptr; | ||
context2.interrupted = false; | ||
context2.connection = connection; | ||
context = &context2; | ||
|
||
epoll_event connectEvent; | ||
connectEvent.data.fd = connection; | ||
connectEvent.events = EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLONESHOT; | ||
connectEvent.data.ptr = context; | ||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_ADD, connection, &connectEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
dispatcher->yield(); | ||
assert(dispatcher != nullptr); | ||
assert(context2.writeContext == dispatcher->getCurrentContext()); | ||
assert(context == &context2); | ||
context = nullptr; | ||
context2.writeContext = nullptr; | ||
if (context2.interrupted) { | ||
if (close(connection) == -1) { | ||
std::cerr << "close failed, errno=" << errno << std::endl; | ||
} | ||
|
||
throw InterruptedException(); | ||
} | ||
|
||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, connection, NULL) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
int retval = -1; | ||
socklen_t retValLen = sizeof(retval); | ||
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); | ||
if (s == -1) { | ||
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
if (retval != 0) { | ||
std::cerr << "connect failed; getsockopt retval = " << retval << std::endl; | ||
} else { | ||
return TcpConnection(*dispatcher, connection); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} else { | ||
return TcpConnection(*dispatcher, connection); | ||
} | ||
} | ||
} | ||
|
||
if (close(connection) == -1) { | ||
std::cerr << "close failed, errno=" << errno << std::endl; | ||
} | ||
} | ||
} | ||
|
||
throw std::runtime_error("TcpConnector::connect"); | ||
} | ||
|
||
void TcpConnector::stop() { | ||
assert(dispatcher != nullptr); | ||
assert(!stopped); | ||
if (context != nullptr) { | ||
ConnectorContext* context2 = static_cast<ConnectorContext*>(context); | ||
if (!context2->interrupted) { | ||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, context2->connection, NULL) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpConnector::stop"); | ||
} | ||
|
||
dispatcher->pushContext(context2->writeContext); | ||
context2->interrupted = true; | ||
} | ||
} | ||
|
||
stopped = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <string> | ||
#include <stdint.h> | ||
|
||
namespace System { | ||
|
||
class Dispatcher; | ||
class TcpConnection; | ||
|
||
class TcpConnector { | ||
public: | ||
TcpConnector(); | ||
TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port); | ||
TcpConnector(const TcpConnector&) = delete; | ||
TcpConnector(TcpConnector&& other); | ||
~TcpConnector(); | ||
TcpConnector& operator=(const TcpConnector&) = delete; | ||
TcpConnector& operator=(TcpConnector&& other); | ||
void start(); | ||
void stop(); | ||
TcpConnection connect(); | ||
|
||
private: | ||
Dispatcher* dispatcher; | ||
std::string address; | ||
uint16_t port; | ||
bool stopped; | ||
void* context; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#include "TcpListener.h" | ||
|
||
#include <sys/epoll.h> | ||
#include <unistd.h> | ||
#include <netdb.h> | ||
#include <fcntl.h> | ||
#include <assert.h> | ||
#include <iostream> | ||
#include <cassert> | ||
#include <errno.h> | ||
#include <stdexcept> | ||
#include "Dispatcher.h" | ||
#include "TcpConnection.h" | ||
#include "InterruptedException.h" | ||
|
||
using namespace System; | ||
|
||
namespace { | ||
|
||
struct ListenerContext : public Dispatcher::ContextExt { | ||
bool interrupted; | ||
}; | ||
|
||
} | ||
|
||
TcpListener::TcpListener() : dispatcher(nullptr) { | ||
} | ||
|
||
TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { | ||
if (other.dispatcher != nullptr) { | ||
listener = other.listener; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
} | ||
|
||
TcpListener::~TcpListener() { | ||
if (dispatcher != nullptr) { | ||
assert(context == nullptr); | ||
if (close(listener) == -1) { | ||
std::cerr << "close() failed, errno=" << errno << '.' << std::endl; | ||
} | ||
} | ||
} | ||
|
||
TcpListener& TcpListener::operator=(TcpListener&& other) { | ||
if (dispatcher != nullptr) { | ||
assert(context == nullptr); | ||
if (close(listener) == -1) { | ||
std::cerr << "close() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpListener::operator="); | ||
} | ||
} | ||
|
||
dispatcher = other.dispatcher; | ||
if (other.dispatcher != nullptr) { | ||
listener = other.listener; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
|
||
return *this; | ||
} | ||
|
||
void TcpListener::start() { | ||
assert(dispatcher != nullptr); | ||
assert(stopped); | ||
stopped = false; | ||
} | ||
|
||
TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) { | ||
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
if (listener == -1) { | ||
std::cerr << "socket failed, errno=" << errno << std::endl; | ||
} else { | ||
int flags = fcntl(listener, F_GETFL, 0); | ||
if (flags == -1 || fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1) { | ||
std::cerr << "fcntl() failed errno=" << errno << std::endl; | ||
} else { | ||
sockaddr_in address; | ||
address.sin_family = AF_INET; | ||
address.sin_port = htons(port); | ||
address.sin_addr.s_addr = INADDR_ANY; | ||
if (bind(listener, reinterpret_cast<sockaddr*>(&address), sizeof address) != 0) { | ||
std::cerr << "bind failed, errno=" << errno << std::endl; | ||
} else if (listen(listener, SOMAXCONN) != 0) { | ||
std::cerr << "listen failed, errno=" << errno << std::endl; | ||
} else { | ||
epoll_event listenEvent; | ||
listenEvent.data.fd = listener; | ||
listenEvent.events = 0; | ||
listenEvent.data.ptr = nullptr; | ||
|
||
if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, listener, &listenEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
stopped = false; | ||
context = nullptr; | ||
return; | ||
} | ||
} | ||
} | ||
|
||
if (close(listener) == -1) { | ||
std::cerr << "close failed, errno=" << errno << std::endl; | ||
} | ||
} | ||
|
||
throw std::runtime_error("TcpListener::TcpListener"); | ||
} | ||
|
||
TcpConnection TcpListener::accept() { | ||
assert(dispatcher != nullptr); | ||
assert(context == nullptr); | ||
if (stopped) { | ||
throw InterruptedException(); | ||
} | ||
|
||
ListenerContext context2; | ||
context2.context = dispatcher->getCurrentContext(); | ||
context2.writeContext = nullptr; | ||
context2.interrupted = false; | ||
|
||
epoll_event listenEvent; | ||
listenEvent.data.fd = listener; | ||
listenEvent.events = EPOLLIN | EPOLLONESHOT; | ||
listenEvent.data.ptr = &context2; | ||
|
||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
context = &context2; | ||
dispatcher->yield(); | ||
assert(dispatcher != nullptr); | ||
assert(context2.context == dispatcher->getCurrentContext()); | ||
assert(context2.writeContext == nullptr); | ||
assert(context == &context2); | ||
context = nullptr; | ||
context2.context = nullptr; | ||
if (context2.interrupted) { | ||
if (close(listener) == -1) { | ||
std::cerr << "close failed, errno=" << errno << std::endl; | ||
} | ||
throw InterruptedException(); | ||
} | ||
|
||
sockaddr inAddr; | ||
socklen_t inLen = sizeof(inAddr); | ||
int connection = ::accept(listener, &inAddr, &inLen); | ||
if (connection == -1) { | ||
std::cerr << "accept() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
int flags = fcntl(connection, F_GETFL, 0); | ||
if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { | ||
std::cerr << "fcntl() failed errno=" << errno << std::endl; | ||
} else { | ||
return TcpConnection(*dispatcher, connection); | ||
} | ||
} | ||
} | ||
|
||
throw std::runtime_error("TcpListener::accept"); | ||
} | ||
|
||
void TcpListener::stop() { | ||
assert(dispatcher != nullptr); | ||
assert(!stopped); | ||
if (context != nullptr) { | ||
ListenerContext* context2 = static_cast<ListenerContext*>(context); | ||
if (!context2->interrupted) { | ||
context2->interrupted = true; | ||
|
||
epoll_event listenEvent; | ||
listenEvent.data.fd = listener; | ||
listenEvent.events = 0; | ||
listenEvent.data.ptr = nullptr; | ||
|
||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpListener::stop"); | ||
} | ||
|
||
dispatcher->pushContext(context2->context); | ||
} | ||
} | ||
|
||
stopped = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <string> | ||
#include <stdint.h> | ||
|
||
namespace System { | ||
|
||
class Dispatcher; | ||
class TcpConnection; | ||
|
||
class TcpListener { | ||
public: | ||
TcpListener(); | ||
TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port); | ||
TcpListener(const TcpListener&) = delete; | ||
TcpListener(TcpListener&& other); | ||
~TcpListener(); | ||
TcpListener& operator=(const TcpListener&) = delete; | ||
TcpListener& operator=(TcpListener&& other); | ||
void start(); | ||
void stop(); | ||
TcpConnection accept(); | ||
|
||
private: | ||
Dispatcher* dispatcher; | ||
int listener; | ||
bool stopped; | ||
void* context; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#include "Timer.h" | ||
#include <sys/timerfd.h> | ||
#include <sys/epoll.h> | ||
#include <assert.h> | ||
#include <unistd.h> | ||
#include <iostream> | ||
#include <stdexcept> | ||
#include <errno.h> | ||
#include "Dispatcher.h" | ||
#include "InterruptedException.h" | ||
|
||
using namespace System; | ||
|
||
namespace { | ||
|
||
struct TimerContext : public Dispatcher::ContextExt { | ||
Dispatcher* dispatcher; | ||
bool interrupted; | ||
}; | ||
|
||
} | ||
|
||
Timer::Timer() : dispatcher(nullptr) { | ||
} | ||
|
||
Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { | ||
timer = timerfd_create(CLOCK_MONOTONIC, 0); | ||
epoll_event timerEvent; | ||
timerEvent.data.fd = timer; | ||
timerEvent.events = 0; | ||
timerEvent.data.ptr = nullptr; | ||
|
||
if (epoll_ctl(this->dispatcher->getEpoll(), EPOLL_CTL_ADD, timer, &timerEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("Timer::Timer"); | ||
} | ||
} | ||
|
||
Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { | ||
if (other.dispatcher != nullptr) { | ||
timer = other.timer; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
} | ||
|
||
Timer::~Timer() { | ||
if (dispatcher != nullptr) { | ||
close(timer); | ||
} | ||
} | ||
|
||
Timer& Timer::operator=(Timer&& other) { | ||
if (dispatcher != nullptr) { | ||
assert(context == nullptr); | ||
close(timer); | ||
} | ||
|
||
dispatcher = other.dispatcher; | ||
if (other.dispatcher != nullptr) { | ||
timer = other.timer; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
|
||
return *this; | ||
} | ||
|
||
void Timer::start() { | ||
assert(dispatcher != nullptr); | ||
assert(stopped); | ||
stopped = false; | ||
} | ||
|
||
void Timer::sleep(std::chrono::milliseconds duration) { | ||
assert(dispatcher != nullptr); | ||
assert(context == nullptr); | ||
if (stopped) { | ||
throw InterruptedException(); | ||
} | ||
|
||
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration); | ||
|
||
itimerspec expires; | ||
expires.it_interval.tv_nsec = expires.it_interval.tv_sec = 0; | ||
expires.it_value.tv_sec = seconds.count(); | ||
expires.it_value.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(duration - seconds).count(); | ||
timerfd_settime(timer, 0, &expires, NULL); | ||
|
||
TimerContext context2; | ||
context2.dispatcher = dispatcher; | ||
context2.context = dispatcher->getCurrentContext(); | ||
context2.writeContext = nullptr; | ||
context2.interrupted = false; | ||
|
||
epoll_event timerEvent; | ||
timerEvent.data.fd = timer; | ||
timerEvent.events = EPOLLIN | EPOLLONESHOT; | ||
timerEvent.data.ptr = &context2; | ||
|
||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("Timer::sleep"); | ||
} | ||
|
||
context = &context2; | ||
dispatcher->yield(); | ||
assert(dispatcher != nullptr); | ||
assert(context2.context == dispatcher->getCurrentContext()); | ||
assert(context2.writeContext == nullptr); | ||
assert(context == &context2); | ||
context = nullptr; | ||
context2.context = nullptr; | ||
if (context2.interrupted) { | ||
throw InterruptedException(); | ||
} | ||
} | ||
|
||
void Timer::stop() { | ||
assert(dispatcher != nullptr); | ||
assert(!stopped); | ||
if (context != nullptr) { | ||
TimerContext* context2 = reinterpret_cast<TimerContext*>(context); | ||
if (context2->context != nullptr) { | ||
epoll_event timerEvent; | ||
timerEvent.data.fd = timer; | ||
timerEvent.events = 0; | ||
timerEvent.data.ptr = nullptr; | ||
|
||
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { | ||
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("Timer::sleep"); | ||
} | ||
|
||
dispatcher->pushContext(context2->context); | ||
context2->interrupted = true; | ||
} | ||
} | ||
|
||
stopped = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <chrono> | ||
|
||
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 start(); | ||
void stop(); | ||
void sleep(std::chrono::milliseconds duration); | ||
|
||
private: | ||
Dispatcher* dispatcher; | ||
int timer; | ||
bool stopped; | ||
void* context; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#include "Dispatcher.h" | ||
#include <iostream> | ||
#define _XOPEN_SOURCE | ||
#include <ucontext.h> | ||
#include <unistd.h> | ||
#include <sys/types.h> | ||
#include <sys/event.h> | ||
#include <assert.h> | ||
#include <sys/time.h> | ||
|
||
using namespace System; | ||
|
||
void Dispatcher::contextProcedureStatic(void *context) { | ||
reinterpret_cast<Dispatcher*>(context)->contextProcedure(); | ||
} | ||
|
||
Dispatcher::Dispatcher() : lastCreatedTimer(0) { | ||
kqueue = ::kqueue(); | ||
if (kqueue == -1) { | ||
std::cerr << "kqueue() fail errno=" << errno << std::endl; | ||
} else { | ||
currentContext = new ucontext_t; | ||
if (getcontext(reinterpret_cast<ucontext_t*>(currentContext)) == -1) { | ||
std::cerr << "getcontext() fail errno=" << errno << std::endl; | ||
} else { | ||
contextCount = 0; | ||
return; | ||
} | ||
} | ||
throw std::runtime_error("Dispatcher::Dispatcher"); | ||
} | ||
|
||
Dispatcher::~Dispatcher() { | ||
assert(resumingContexts.empty()); | ||
assert(reusableContexts.size() == contextCount); | ||
assert(spawningProcedures.empty()); | ||
assert(reusableContexts.size() == allocatedStacks.size()); | ||
while (!reusableContexts.empty()) { | ||
delete[] allocatedStacks.top(); | ||
allocatedStacks.pop(); | ||
delete static_cast<ucontext_t*>(reusableContexts.top()); | ||
reusableContexts.pop(); | ||
} | ||
|
||
while (!timers.empty()) { | ||
timers.pop(); | ||
} | ||
|
||
if (-1 == close(kqueue)) { | ||
std::cerr << "close() fail errno=" << errno << std::endl; | ||
} | ||
} | ||
|
||
void* Dispatcher::getCurrentContext() const { | ||
return currentContext; | ||
} | ||
|
||
int Dispatcher::getKqueue() const { | ||
return kqueue; | ||
} | ||
|
||
void Dispatcher::pushContext(void* context) { | ||
resumingContexts.push(context); | ||
} | ||
|
||
void Dispatcher::spawn(std::function<void()>&& procedure) { | ||
void* context; | ||
if (reusableContexts.empty()) { | ||
context = new ucontext_t; | ||
if (-1 == getcontext(reinterpret_cast<ucontext_t *>(context))) { //makecontext precondition | ||
std::cerr << "getcontext() fail errno=" << errno << std::endl; | ||
throw std::runtime_error("Dispatcher::spawn()"); | ||
} | ||
auto stackPointer = new uint8_t[64 * 1024]; | ||
reinterpret_cast<ucontext_t *>(context)->uc_stack.ss_sp = stackPointer; | ||
allocatedStacks.push(stackPointer); | ||
reinterpret_cast<ucontext_t *>(context)->uc_stack.ss_size = 64 * 1024; | ||
makecontext(reinterpret_cast<ucontext_t *>(context), (void(*)())contextProcedureStatic, 1, reinterpret_cast<int*>(this)); | ||
++contextCount; | ||
} else { | ||
context = reusableContexts.top(); | ||
reusableContexts.pop(); | ||
} | ||
|
||
resumingContexts.push(context); | ||
spawningProcedures.emplace(std::move(procedure)); | ||
} | ||
|
||
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::clear() { | ||
//TODO | ||
} | ||
|
||
void Dispatcher::yield() { | ||
void* context; | ||
for (;;) { | ||
if (!resumingContexts.empty()) { | ||
context = resumingContexts.front(); | ||
resumingContexts.pop(); | ||
break; | ||
} | ||
|
||
struct kevent event; | ||
int count = kevent(kqueue, NULL, 0, &event, 1, NULL); | ||
|
||
if (count == 1) { | ||
context = static_cast<ContextExt*>(event.udata)->context; | ||
break; | ||
} | ||
|
||
if (errno != EINTR) { | ||
std::cerr << "kevent() failed, errno=" << errno << std::endl; | ||
throw std::runtime_error("Dispatcher::yield()"); | ||
} | ||
} | ||
|
||
if (context != currentContext) { | ||
ucontext_t* oldContext = static_cast<ucontext_t*>(currentContext); | ||
currentContext = context; | ||
if (-1 == swapcontext(oldContext, static_cast<ucontext_t *>(context))) { | ||
std::cerr << "setcontext() failed, errno=" << errno << std::endl; | ||
throw std::runtime_error("Dispatcher::yield()"); | ||
} | ||
} | ||
} | ||
|
||
void Dispatcher::contextProcedure() { | ||
void* context = currentContext; | ||
for (;;) { | ||
assert(!spawningProcedures.empty()); | ||
std::function<void()> procedure = std::move(spawningProcedures.front()); | ||
spawningProcedures.pop(); | ||
procedure(); | ||
reusableContexts.push(context); | ||
yield(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <functional> | ||
#include <queue> | ||
#include <stack> | ||
|
||
namespace System { | ||
|
||
class Dispatcher { | ||
public: | ||
Dispatcher(); | ||
Dispatcher(const Dispatcher&) = delete; | ||
~Dispatcher(); | ||
Dispatcher& operator=(const Dispatcher&) = delete; | ||
void spawn(std::function<void()>&& procedure); | ||
void yield(); | ||
void clear(); | ||
|
||
struct ContextExt { | ||
void *context; | ||
}; | ||
private: | ||
friend class Event; | ||
friend class DispatcherAccessor; | ||
friend class TcpConnection; | ||
friend class TcpConnector; | ||
friend class TcpListener; | ||
friend class Timer; | ||
int kqueue; | ||
void* currentContext; | ||
int lastCreatedTimer; | ||
std::size_t contextCount; | ||
std::queue<void*> resumingContexts; | ||
std::stack<void*> reusableContexts; | ||
std::stack<uint8_t *> allocatedStacks; | ||
std::queue<std::function<void()>> spawningProcedures; | ||
std::stack<int> timers; | ||
|
||
int getKqueue() const; | ||
int getTimer(); | ||
void pushTimer(int timer); | ||
void pushContext(void* context); | ||
void* getCurrentContext() const; | ||
|
||
void contextProcedure(); | ||
static void contextProcedureStatic(void* context); | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#include "Event.h" | ||
#include <cassert> | ||
#include <iostream> | ||
#include "Dispatcher.h" | ||
|
||
using namespace System; | ||
|
||
namespace { | ||
|
||
struct Waiter { | ||
Waiter* next; | ||
void* context; | ||
}; | ||
|
||
} | ||
|
||
Event::Event() : dispatcher(nullptr) { | ||
} | ||
|
||
Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) { | ||
} | ||
|
||
Event::Event(Event&& other) : dispatcher(other.dispatcher) { | ||
if (other.dispatcher != nullptr) { | ||
first = other.first; | ||
if (other.first != nullptr) { | ||
last = other.last; | ||
} | ||
|
||
state = other.state; | ||
other.dispatcher = nullptr; | ||
} | ||
} | ||
|
||
Event::~Event() { | ||
assert(first == nullptr); | ||
} | ||
|
||
Event& Event::operator=(Event&& other) { | ||
assert(first == nullptr); | ||
dispatcher = other.dispatcher; | ||
if (other.dispatcher != nullptr) { | ||
first = other.first; | ||
if (other.first != nullptr) { | ||
last = other.last; | ||
} | ||
|
||
state = other.state; | ||
other.dispatcher = nullptr; | ||
} | ||
|
||
return *this; | ||
} | ||
|
||
bool Event::get() const { | ||
assert(dispatcher != nullptr); | ||
return state; | ||
} | ||
|
||
void Event::clear() { | ||
assert(dispatcher != nullptr); | ||
state = false; | ||
} | ||
|
||
void Event::set() { | ||
assert(dispatcher != nullptr); | ||
state = true; | ||
for (Waiter* waiter = static_cast<Waiter*>(first); waiter != nullptr; waiter = waiter->next) { | ||
dispatcher->pushContext(waiter->context); | ||
} | ||
|
||
first = nullptr; | ||
} | ||
|
||
void Event::wait() { | ||
assert(dispatcher != nullptr); | ||
if (!state) { | ||
Waiter waiter = { nullptr, dispatcher->getCurrentContext() }; | ||
if (first != nullptr) { | ||
static_cast<Waiter*>(last)->next = &waiter; | ||
} else { | ||
first = &waiter; | ||
} | ||
|
||
last = &waiter; | ||
dispatcher->yield(); | ||
assert(dispatcher != nullptr); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
namespace System { | ||
|
||
class Dispatcher; | ||
|
||
class Event { | ||
public: | ||
Event(); | ||
explicit Event(Dispatcher& dispatcher); | ||
Event(const Event&) = delete; | ||
Event(Event&& other); | ||
~Event(); | ||
Event& operator=(const Event&) = delete; | ||
Event& operator=(Event&& other); | ||
bool get() const; | ||
void clear(); | ||
void set(); | ||
void wait(); | ||
|
||
private: | ||
Dispatcher* dispatcher; | ||
void* first; | ||
void* last; | ||
bool state; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#include "InterruptedException.h" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <exception> | ||
|
||
namespace System { | ||
|
||
class InterruptedException : public std::exception { | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#include "TcpConnection.h" | ||
#include <unistd.h> | ||
#include <assert.h> | ||
#include <iostream> | ||
#include <sys/socket.h> | ||
#include <sys/event.h> | ||
#include <sys/socket.h> | ||
#include "Dispatcher.h" | ||
#include "InterruptedException.h" | ||
|
||
using namespace System; | ||
|
||
namespace { | ||
|
||
struct ConnectionContext : public Dispatcher::ContextExt { | ||
bool interrupted; | ||
}; | ||
|
||
} | ||
|
||
TcpConnection::TcpConnection() : dispatcher(nullptr) { | ||
} | ||
|
||
TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), readContext(nullptr), writeContext(nullptr) { | ||
} | ||
|
||
TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) { | ||
if (other.dispatcher != nullptr) { | ||
connection = other.connection; | ||
stopped = other.stopped; | ||
readContext = other.readContext; | ||
writeContext = other.writeContext; | ||
other.dispatcher = nullptr; | ||
} | ||
} | ||
|
||
TcpConnection::~TcpConnection() { | ||
if (dispatcher != nullptr) { | ||
assert(readContext == nullptr); | ||
assert(writeContext == nullptr); | ||
if (close(connection) == -1) { | ||
std::cerr << "close() failed, errno=" << errno << '.' << std::endl; | ||
} | ||
} | ||
} | ||
|
||
TcpConnection& TcpConnection::operator=(TcpConnection&& other) { | ||
if (dispatcher != nullptr) { | ||
assert(readContext == nullptr); | ||
assert(writeContext == nullptr); | ||
if (close(connection) == -1) { | ||
std::cerr << "close() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpConnection::operator="); | ||
} | ||
} | ||
|
||
dispatcher = other.dispatcher; | ||
if (other.dispatcher != nullptr) { | ||
connection = other.connection; | ||
stopped = other.stopped; | ||
readContext = other.readContext; | ||
writeContext = other.writeContext; | ||
other.dispatcher = nullptr; | ||
} | ||
|
||
return *this; | ||
} | ||
|
||
void TcpConnection::start() { | ||
assert(dispatcher != nullptr); | ||
assert(stopped); | ||
stopped = false; | ||
} | ||
|
||
size_t TcpConnection::read(uint8_t* data, size_t size) { | ||
assert(dispatcher != nullptr); | ||
assert(readContext == nullptr); | ||
if (stopped) { | ||
throw InterruptedException(); | ||
} | ||
|
||
ssize_t transferred = ::recv(connection, (void *)data, size, 0); | ||
if (transferred == -1) { | ||
if (errno != EAGAIN && errno != EWOULDBLOCK) { | ||
std::cerr << "recv failed, result=" << errno << '.' << std::endl; | ||
} else { | ||
ConnectionContext context2; | ||
context2.context = dispatcher->getCurrentContext(); | ||
context2.interrupted = false; | ||
struct kevent event; | ||
EV_SET(&event, connection, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT, 0, 0, &context2); | ||
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
readContext = &context2; | ||
dispatcher->yield(); | ||
assert(dispatcher != nullptr); | ||
assert(context2.context == dispatcher->getCurrentContext()); | ||
assert(readContext == &context2); | ||
readContext = nullptr; | ||
context2.context = nullptr; | ||
if (context2.interrupted) { | ||
throw InterruptedException(); | ||
} | ||
|
||
ssize_t transferred = ::recv(connection, (void *)data, size, 0); | ||
if (transferred == -1) { | ||
std::cerr << "recv failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
if (transferred == 0) { | ||
std::cerr << "recv return after yield with 0 bytes" << std::endl; | ||
|
||
int retval = -1; | ||
socklen_t retValLen = sizeof(retval); | ||
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); | ||
if (s == -1) { | ||
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
std::cerr << "recv getsockopt retval = " << retval << std::endl; | ||
} | ||
} | ||
|
||
assert(transferred <= size); | ||
return transferred; | ||
} | ||
} | ||
} | ||
|
||
throw std::runtime_error("TcpConnection::read"); | ||
} | ||
|
||
assert(transferred <= size); | ||
return transferred; | ||
} | ||
|
||
void TcpConnection::write(const uint8_t* data, size_t size) { | ||
assert(dispatcher != nullptr); | ||
assert(writeContext == nullptr); | ||
if (stopped) { | ||
throw InterruptedException(); | ||
} | ||
|
||
if (size == 0) { | ||
if (shutdown(connection, SHUT_WR) == -1) { | ||
std::cerr << "shutdown failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpConnection::write"); | ||
} | ||
|
||
return; | ||
} | ||
|
||
ssize_t transferred = ::send(connection, (void *)data, size, 0); | ||
if (transferred == -1) { | ||
if (errno != EAGAIN && errno != EWOULDBLOCK) { | ||
std::cerr << "send failed, result=" << errno << '.' << std::endl; | ||
} else { | ||
ConnectionContext context2; | ||
context2.context = dispatcher->getCurrentContext(); | ||
context2.interrupted = false; | ||
struct kevent event; | ||
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, &context2); | ||
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
writeContext = &context2; | ||
dispatcher->yield(); | ||
assert(dispatcher != nullptr); | ||
assert(context2.context == dispatcher->getCurrentContext()); | ||
assert(writeContext == &context2); | ||
writeContext = nullptr; | ||
context2.context = nullptr; | ||
if (context2.interrupted) { | ||
throw InterruptedException(); | ||
} | ||
|
||
ssize_t transferred = ::send(connection, (void *)data, size, 0); | ||
if (transferred == -1) { | ||
std::cerr << "recv failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
if (transferred == 0) { | ||
throw std::runtime_error("send transferred 0 bytes."); | ||
} | ||
|
||
assert(transferred == size); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
throw std::runtime_error("TcpConnection::write"); | ||
} | ||
} | ||
|
||
void TcpConnection::stop() { | ||
assert(dispatcher != nullptr); | ||
assert(!stopped); | ||
if (writeContext != nullptr && static_cast<ConnectionContext*>(writeContext)->context != nullptr) { | ||
ConnectionContext* context2 = static_cast<ConnectionContext*>(writeContext); | ||
if (!context2->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) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpListener::stop"); | ||
} | ||
|
||
context2->interrupted = true; | ||
dispatcher->pushContext(context2->context); | ||
} | ||
} | ||
|
||
if (readContext != nullptr && static_cast<ConnectionContext*>(readContext)->context != nullptr) { | ||
ConnectionContext* context2 = static_cast<ConnectionContext*>(readContext); | ||
if (!context2->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) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpListener::stop"); | ||
} | ||
|
||
context2->interrupted = true; | ||
dispatcher->pushContext(context2->context); | ||
} | ||
} | ||
|
||
stopped = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include <cstddef> | ||
#include <cstdint> | ||
|
||
namespace System { | ||
|
||
class Dispatcher; | ||
|
||
class TcpConnection { | ||
public: | ||
TcpConnection(); | ||
TcpConnection(const TcpConnection&) = delete; | ||
TcpConnection(TcpConnection&& other); | ||
~TcpConnection(); | ||
TcpConnection& operator=(const TcpConnection&) = delete; | ||
TcpConnection& operator=(TcpConnection&& other); | ||
void start(); | ||
void stop(); | ||
std::size_t read(uint8_t* data, std::size_t size); | ||
void write(const uint8_t* data, std::size_t size); | ||
|
||
private: | ||
friend class TcpConnector; | ||
friend class TcpListener; | ||
|
||
explicit TcpConnection(Dispatcher& dispatcher, int socket); | ||
|
||
Dispatcher* dispatcher; | ||
int connection; | ||
bool stopped; | ||
void* readContext; | ||
void* writeContext; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/> | ||
|
||
#include "TcpConnector.h" | ||
#include <cassert> | ||
#include <iostream> | ||
#include <random> | ||
#include <sstream> | ||
#include <sys/event.h> | ||
#include <sys/types.h> | ||
#include <sys/socket.h> | ||
#include <netinet/in.h> | ||
#include <netdb.h> | ||
#include <fcntl.h> | ||
#include <unistd.h> | ||
#include "InterruptedException.h" | ||
#include "Dispatcher.h" | ||
#include "TcpConnection.h" | ||
|
||
using namespace System; | ||
|
||
namespace { | ||
|
||
struct ConnectorContext : public Dispatcher::ContextExt { | ||
int connection; | ||
bool interrupted; | ||
}; | ||
|
||
} | ||
|
||
TcpConnector::TcpConnector() : dispatcher(nullptr) { | ||
} | ||
|
||
TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) { | ||
} | ||
|
||
TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { | ||
if (other.dispatcher != nullptr) { | ||
address = other.address; | ||
port = other.port; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
} | ||
|
||
TcpConnector::~TcpConnector() { | ||
} | ||
|
||
TcpConnector& TcpConnector::operator=(TcpConnector&& other) { | ||
dispatcher = other.dispatcher; | ||
if (other.dispatcher != nullptr) { | ||
address = other.address; | ||
port = other.port; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
|
||
return *this; | ||
} | ||
|
||
void TcpConnector::start() { | ||
assert(dispatcher != nullptr); | ||
assert(stopped); | ||
stopped = false; | ||
} | ||
|
||
void TcpConnector::stop() { | ||
assert(dispatcher != nullptr); | ||
assert(!stopped); | ||
if (context != nullptr) { | ||
ConnectorContext* context2 = static_cast<ConnectorContext*>(context); | ||
if (!context2->interrupted) { | ||
struct kevent event; | ||
EV_SET(&event, context2->connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); | ||
|
||
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpConnector::stop"); | ||
} | ||
|
||
dispatcher->pushContext(context2->context); | ||
context2->interrupted = true; | ||
} | ||
} | ||
|
||
stopped = true; | ||
} | ||
|
||
TcpConnection TcpConnector::connect() { | ||
assert(dispatcher != nullptr); | ||
assert(context == nullptr); | ||
if (stopped) { | ||
throw InterruptedException(); | ||
} | ||
|
||
std::ostringstream portStream; | ||
portStream << port; | ||
addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL }; | ||
addrinfo *addressInfos; | ||
int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos); | ||
if (result == -1) { | ||
std::cerr << "getaddrinfo failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
std::size_t count = 0; | ||
for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { | ||
++count; | ||
} | ||
|
||
std::random_device randomDevice; | ||
std::mt19937 generator(randomDevice()); | ||
std::uniform_int_distribution<std::size_t> distribution(0, count - 1); | ||
std::size_t index = distribution(generator); | ||
addrinfo* addressInfo = addressInfos; | ||
for (std::size_t i = 0; i < index; ++i) { | ||
addressInfo = addressInfo->ai_next; | ||
} | ||
|
||
sockaddr_in addressData = *reinterpret_cast<sockaddr_in*>(addressInfo->ai_addr); | ||
freeaddrinfo(addressInfo); | ||
int connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
if (connection == -1) { | ||
std::cerr << "socket failed, errno=" << errno << '.' << std::endl; | ||
} 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<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) { | ||
std::cerr << "bind failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
int flags = fcntl(connection, F_GETFL, 0); | ||
if (flags == -1 || (fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1)) { | ||
std::cerr << "fcntl() failed errno=" << errno << std::endl; | ||
} else { | ||
int result = ::connect(connection, reinterpret_cast<sockaddr *>(&addressData), sizeof addressData); | ||
if (result == -1) { | ||
if (errno == EINPROGRESS) { | ||
|
||
ConnectorContext context2; | ||
context2.context = dispatcher->getCurrentContext(); | ||
context2.interrupted = false; | ||
context2.connection = connection; | ||
|
||
struct kevent event; | ||
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT | EV_CLEAR, 0, 0, &context2); | ||
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
context = &context2; | ||
dispatcher->yield(); | ||
assert(dispatcher != nullptr); | ||
assert(context2.context == dispatcher->getCurrentContext()); | ||
assert(context == &context2); | ||
context = nullptr; | ||
context2.context = nullptr; | ||
if (context2.interrupted) { | ||
if (close(connection) == -1) { | ||
std::cerr << "close failed, errno=" << errno << std::endl; | ||
} | ||
|
||
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) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
int retval = -1; | ||
socklen_t retValLen = sizeof(retval); | ||
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); | ||
if (s == -1) { | ||
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
if (retval != 0) { | ||
std::cerr << "connect failed; getsockopt retval = " << retval << std::endl; | ||
} else { | ||
return TcpConnection(*dispatcher, connection); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} else { | ||
return TcpConnection(*dispatcher, connection); | ||
} | ||
} | ||
} | ||
|
||
if (close(connection) == -1) { | ||
std::cerr << "close failed, errno=" << errno << std::endl; | ||
} | ||
} | ||
} | ||
|
||
throw std::runtime_error("TcpConnector::connect"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers | ||
// | ||
// This file is part of Bytecoin. | ||
// | ||
// Bytecoin 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. | ||
// | ||
// Bytecoin 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 Bytecoin. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#include "TcpListener.h" | ||
#include <cassert> | ||
#include <iostream> | ||
#include <unistd.h> | ||
#include <fcntl.h> | ||
#include <sys/event.h> | ||
#include <sys/types.h> | ||
#include <sys/socket.h> | ||
#include <netinet/in.h> | ||
#include "InterruptedException.h" | ||
#include "Dispatcher.h" | ||
#include "TcpConnection.h" | ||
|
||
using namespace System; | ||
|
||
namespace { | ||
|
||
struct ListenerContext : public Dispatcher::ContextExt { | ||
bool interrupted; | ||
}; | ||
|
||
} | ||
|
||
TcpListener::TcpListener() : dispatcher(nullptr) { | ||
} | ||
|
||
TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { | ||
if (other.dispatcher != nullptr) { | ||
listener = other.listener; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
} | ||
|
||
TcpListener::~TcpListener() { | ||
if (dispatcher != nullptr) { | ||
assert(context == nullptr); | ||
if (close(listener) == -1) { | ||
std::cerr << "close() failed, errno=" << errno << '.' << std::endl; | ||
} | ||
} | ||
} | ||
|
||
TcpListener& TcpListener::operator=(TcpListener&& other) { | ||
if (dispatcher != nullptr) { | ||
assert(context == nullptr); | ||
if (close(listener) == -1) { | ||
std::cerr << "close() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpListener::operator="); | ||
} | ||
} | ||
|
||
dispatcher = other.dispatcher; | ||
if (other.dispatcher != nullptr) { | ||
listener = other.listener; | ||
stopped = other.stopped; | ||
context = other.context; | ||
other.dispatcher = nullptr; | ||
} | ||
|
||
return *this; | ||
} | ||
|
||
void TcpListener::start() { | ||
assert(dispatcher != nullptr); | ||
assert(stopped); | ||
stopped = false; | ||
} | ||
|
||
TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) { | ||
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
if (listener == -1) { | ||
std::cerr << "socket failed, errno=" << errno << std::endl; | ||
} else { | ||
int flags = fcntl(listener, F_GETFL, 0); | ||
if (flags == -1 || (fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1)) { | ||
std::cerr << "fcntl() failed errno=" << errno << std::endl; | ||
} else { | ||
sockaddr_in address; | ||
address.sin_family = AF_INET; | ||
address.sin_port = htons(port); | ||
address.sin_addr.s_addr = INADDR_ANY; | ||
if (bind(listener, reinterpret_cast<sockaddr*>(&address), sizeof address) != 0) { | ||
std::cerr << "bind failed, errno=" << errno << std::endl; | ||
} else if (listen(listener, SOMAXCONN) != 0) { | ||
std::cerr << "listen failed, errno=" << errno << std::endl; | ||
} else { | ||
struct kevent event; | ||
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, SOMAXCONN, NULL); | ||
|
||
if (kevent(dispatcher.getKqueue(), &event, 1, NULL, 0, NULL) == -1) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
stopped = false; | ||
context = nullptr; | ||
return; | ||
} | ||
} | ||
} | ||
|
||
if (close(listener) == -1) { | ||
std::cerr << "close failed, errno=" << errno << std::endl; | ||
} | ||
} | ||
|
||
throw std::runtime_error("TcpListener::TcpListener"); | ||
} | ||
|
||
TcpConnection TcpListener::accept() { | ||
assert(dispatcher != nullptr); | ||
assert(context == nullptr); | ||
if (stopped) { | ||
throw InterruptedException(); | ||
} | ||
|
||
ListenerContext context2; | ||
context2.context = dispatcher->getCurrentContext(); | ||
context2.interrupted = false; | ||
|
||
struct kevent event; | ||
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, SOMAXCONN, &context2); | ||
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
context = &context2; | ||
dispatcher->yield(); | ||
assert(dispatcher != nullptr); | ||
assert(context2.context == dispatcher->getCurrentContext()); | ||
assert(context == &context2); | ||
context = nullptr; | ||
context2.context = nullptr; | ||
if (context2.interrupted) { | ||
if (close(listener) == -1) { | ||
std::cerr << "close failed, errno=" << errno << std::endl; | ||
} | ||
throw InterruptedException(); | ||
} | ||
struct kevent event; | ||
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, 0, NULL); | ||
|
||
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
sockaddr inAddr; | ||
socklen_t inLen = sizeof(inAddr); | ||
int connection = ::accept(listener, &inAddr, &inLen); | ||
if (connection == -1) { | ||
std::cerr << "accept() failed, errno=" << errno << '.' << std::endl; | ||
} else { | ||
int flags = fcntl(connection, F_GETFL, 0); | ||
if (flags == -1 || (fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1)) { | ||
std::cerr << "fcntl() failed errno=" << errno << std::endl; | ||
} else { | ||
return TcpConnection(*dispatcher, connection); | ||
} | ||
} | ||
} | ||
} | ||
|
||
throw std::runtime_error("TcpListener::accept"); | ||
} | ||
|
||
void TcpListener::stop() { | ||
assert(dispatcher != nullptr); | ||
assert(!stopped); | ||
if (context != nullptr) { | ||
ListenerContext* context2 = static_cast<ListenerContext*>(context); | ||
if (!context2->interrupted) { | ||
context2->interrupted = true; | ||
|
||
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) { | ||
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; | ||
throw std::runtime_error("TcpListener::stop"); | ||
} | ||
|
||
dispatcher->pushContext(context2->context); | ||
} | ||
} | ||
|
||
stopped = true; | ||
} |
Oops, something went wrong.