Skip to content

Commit

Permalink
Multisignature API, Low level and High level APIs
Browse files Browse the repository at this point in the history
Antonio Juarez committed Apr 6, 2015
1 parent 257a2bf commit 1743402
Showing 277 changed files with 30,175 additions and 9,760 deletions.
22 changes: 12 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -12,11 +12,19 @@ if(APPLE)
include_directories(SYSTEM /usr/include/malloc)
endif()

if(MSVC)
include_directories(src/Platform/Windows)
elseif(APPLE)
include_directories(src/Platform/OSX)
else()
include_directories(src/Platform/Linux)
endif()


set(STATIC ${MSVC} CACHE BOOL "Link libraries statically")

if(MSVC)
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /D_VARIADIC_MAX=8 /FIinline_c.h /D__SSE4_1__")
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline")
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /D_VARIADIC_MAX=8 /D__SSE4_1__")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760")
if(STATIC)
foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO)
@@ -35,7 +43,7 @@ else()
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration -Wno-error=unused-function")
else()
set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized")
set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized -Wno-error=clobbered -Wno-error=unused-but-set-variable")
endif()
if(MINGW)
set(WARNINGS "${WARNINGS} -Wno-error=unused-value")
@@ -46,13 +54,7 @@ else()
endif()
set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes")
set(CXX_WARNINGS "-Wno-reorder -Wno-missing-field-initializers")
try_compile(STATIC_ASSERT_RES "${CMAKE_CURRENT_BINARY_DIR}/static-assert" "${CMAKE_CURRENT_SOURCE_DIR}/utils/test-static-assert.c" COMPILE_DEFINITIONS "-std=c11")
if(STATIC_ASSERT_RES)
set(STATIC_ASSERT_FLAG "")
else()
set(STATIC_ASSERT_FLAG "-Dstatic_assert=_Static_assert")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} -maes")
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_HAS_TR1_TUPLE=0")
8 changes: 8 additions & 0 deletions ReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
Release notes 1.0.3

- Multisignature API
- Low level API
- High level API improvements
- Instant transaction pool notifications
- Fully refactored simplewallet

Release notes 1.0.2

- Transaction history for simplewallet
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
#pragma once

#include <cstdint>
#include <vector>

#include <boost/mpl/contains.hpp>
#include <boost/mpl/vector.hpp>
2 changes: 2 additions & 0 deletions contrib/epee/include/storages/levin_abstract_invoke2.h
Original file line number Diff line number Diff line change
@@ -114,6 +114,7 @@ namespace epee
const_cast<t_arg&>(out_struct).store(stg);//TODO: add true const support to searilzation
std::string buff_to_send, buff_to_recv;
stg.store_to_binary(buff_to_send);

int res = transport.invoke_async(command, buff_to_send, conn_id, [cb, command](int code, const std::string& buff, typename t_transport::connection_context& context)->bool
{
t_result result_struct = AUTO_VAL_INIT(result_struct);
@@ -131,6 +132,7 @@ namespace epee
return false;
}
result_struct.load(stg_ret);

cb(code, result_struct, context);
return true;
}, inv_timeout);
1 change: 1 addition & 0 deletions contrib/epee/include/storages/portable_storage_from_json.h
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
#pragma once

#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>

#include "parserse_base_utils.h"
#include "file_io_utils.h"
2 changes: 2 additions & 0 deletions contrib/epee/include/string_tools.h
Original file line number Diff line number Diff line change
@@ -38,6 +38,8 @@
#include <string>
#include <sstream>

#include <boost/lexical_cast.hpp>

#include "warnings.h"

#ifndef OUT
10 changes: 10 additions & 0 deletions include/INode.h
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@ class INodeObserver {
virtual void peerCountUpdated(size_t count) {}
virtual void localBlockchainUpdated(uint64_t height) {}
virtual void lastKnownBlockHeightUpdated(uint64_t height) {}
virtual void poolChanged() {}
};

struct OutEntry {
@@ -47,6 +48,12 @@ struct OutsForAmount {
std::vector<OutEntry> outs;
};

struct BlockCompleteEntry {
crypto::hash blockHash;
cryptonote::blobdata block;
std::list<cryptonote::blobdata> txs;
};

class INode {
public:
typedef std::function<void(std::error_code)> Callback;
@@ -61,11 +68,14 @@ class INode {
virtual size_t getPeerCount() const = 0;
virtual uint64_t getLastLocalBlockHeight() const = 0;
virtual uint64_t getLastKnownBlockHeight() const = 0;
virtual uint64_t getLastLocalBlockTimestamp() const = 0;

virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) = 0;
virtual void getRandomOutsByAmounts(std::vector<uint64_t>&& amounts, uint64_t outsCount, std::vector<cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& result, const Callback& callback) = 0;
virtual void getNewBlocks(std::list<crypto::hash>&& knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback) = 0;
virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback) = 0;
virtual void queryBlocks(std::list<crypto::hash>&& knownBlockIds, uint64_t timestamp, std::list<BlockCompleteEntry>& newBlocks, uint64_t& startHeight, const Callback& callback) = 0;
virtual void getPoolSymmetricDifference(std::vector<crypto::hash>&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector<cryptonote::Transaction>& new_txs, std::vector<crypto::hash>& deleted_tx_ids, const Callback& callback) = 0;
};

}
29 changes: 29 additions & 0 deletions include/IObservable.h
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;
};

}
30 changes: 30 additions & 0 deletions include/IStreamSerializable.h
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;
};

}
180 changes: 180 additions & 0 deletions include/ITransaction.h
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() { }

};

}
105 changes: 105 additions & 0 deletions include/ITransfersContainer.h
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;
};

}
75 changes: 75 additions & 0 deletions include/ITransfersSynchronizer.h
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;
};

}
49 changes: 39 additions & 10 deletions include/IWallet.h
Original file line number Diff line number Diff line change
@@ -41,23 +41,48 @@ const TransactionId INVALID_TRANSACTION_ID = std::numeric_limits<TransactionI
const TransferId INVALID_TRANSFER_ID = std::numeric_limits<TransferId>::max();
const uint64_t UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits<uint64_t>::max();

enum class TransactionState : uint8_t {
Active, // --> {Deleted}
Deleted, // --> {Active}

Sending, // --> {Active, Cancelled, Failed}
Cancelled, // --> {}
Failed // --> {}
};

struct TransactionInfo {
TransferId firstTransferId;
size_t transferCount;
int64_t totalAmount;
uint64_t fee;
TransactionHash hash;
bool isCoinbase;
uint64_t blockHeight;
uint64_t timestamp;
std::string extra;
TransferId firstTransferId;
size_t transferCount;
int64_t totalAmount;
uint64_t fee;
uint64_t sentTime;
uint64_t unlockTime;
TransactionHash hash;
bool isCoinbase;
uint64_t blockHeight;
uint64_t timestamp;
std::string extra;
TransactionState state;
};

typedef std::array<uint8_t, 32> WalletPublicKey;
typedef std::array<uint8_t, 32> WalletSecretKey;

struct WalletAccountKeys {
WalletPublicKey viewPublicKey;
WalletSecretKey viewSecretKey;
WalletPublicKey spendPublicKey;
WalletSecretKey spendSecretKey;
};

class IWalletObserver {
public:
virtual ~IWalletObserver() {}

virtual void initCompleted(std::error_code result) {}
virtual void saveCompleted(std::error_code result) {}
virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total, std::error_code result) {}
virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {}
virtual void synchronizationCompleted(std::error_code result) {}
virtual void actualBalanceUpdated(uint64_t actualBalance) {}
virtual void pendingBalanceUpdated(uint64_t pendingBalance) {}
virtual void externalTransactionCreated(TransactionId transactionId) {}
@@ -73,7 +98,9 @@ class IWallet {

virtual void initAndGenerate(const std::string& password) = 0;
virtual void initAndLoad(std::istream& source, const std::string& password) = 0;
virtual void initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password) = 0;
virtual void shutdown() = 0;
virtual void reset() = 0;

virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) = 0;

@@ -95,6 +122,8 @@ class IWallet {
virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0;
virtual TransactionId sendTransaction(const std::vector<Transfer>& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0;
virtual std::error_code cancelTransaction(size_t transferId) = 0;

virtual void getAccountKeys(WalletAccountKeys& keys) = 0;
};

}
35 changes: 29 additions & 6 deletions src/CMakeLists.txt
100644 → 100755
Original file line number Diff line number Diff line change
@@ -12,6 +12,19 @@ file(GLOB_RECURSE CONN_TOOL connectivity_tool/*)
file(GLOB_RECURSE WALLET wallet/*)
file(GLOB_RECURSE MINER miner/*)
file(GLOB_RECURSE NODE_RPC_PROXY node_rpc_proxy/*)
file(GLOB_RECURSE TRANSFERS transfers/*)
if(MSVC)
file(GLOB_RECURSE SYSTEM Platform/Windows/System/*)
elseif(APPLE)
file(GLOB_RECURSE SYSTEM Platform/OSX/System/*)
else()
file(GLOB_RECURSE SYSTEM Platform/Linux/System/*)
endif()
file(GLOB_RECURSE SERIALIZATION serialization/*)
file(GLOB_RECURSE LOGGER logger/*)
file(GLOB_RECURSE INPROCESS_NODE inprocess_node/*)
file(GLOB_RECURSE HTTP HTTP/*)


source_group(common FILES ${COMMON})
source_group(crypto FILES ${CRYPTO})
@@ -20,32 +33,42 @@ source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL})
source_group(daemon FILES ${DAEMON})
source_group(p2p FILES ${P2P})
source_group(rpc FILES ${RPC})
source_group(System FILES ${SYSTEM} ${HTTP})
source_group(simplewallet FILES ${SIMPLEWALLET})
source_group(connectivity-tool FILES ${CONN_TOOL})
source_group(wallet FILES ${WALLET})
source_group(simpleminer FILES ${MINER})
source_group(node_rpc_proxy FILES ${NODE_RPC_PROXY})
source_group(transfers FILES ${TRANSFERS})
source_group(logger FILES ${LOGGER})
source_group(inprocess_node FILES ${INPROCESS_NODE})

add_library(common ${COMMON})
add_library(crypto ${CRYPTO})
add_library(serialization ${SERIALIZATION})
add_library(cryptonote_core ${CRYPTONOTE_CORE})
add_library(node_rpc_proxy ${NODE_RPC_PROXY})
add_library(inprocess_node ${INPROCESS_NODE})
add_executable(daemon ${DAEMON} ${P2P} ${CRYPTONOTE_PROTOCOL})
add_executable(connectivity_tool ${CONN_TOOL})
add_executable(simpleminer ${MINER})
target_link_libraries(daemon epee rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES})
target_link_libraries(connectivity_tool epee cryptonote_core crypto common ${Boost_LIBRARIES})
target_link_libraries(simpleminer epee cryptonote_core crypto common ${Boost_LIBRARIES})
target_link_libraries(daemon epee rpc cryptonote_core crypto common upnpc-static serialization ${Boost_LIBRARIES})
target_link_libraries(connectivity_tool epee rpc cryptonote_core crypto common serialization ${Boost_LIBRARIES})
target_link_libraries(simpleminer epee cryptonote_core crypto common serialization ${Boost_LIBRARIES})
add_library(rpc ${RPC})
add_library(System ${SYSTEM} ${HTTP} System/TcpStream.cpp System/TcpStream.h)
add_library(wallet ${WALLET})
add_executable(simplewallet ${SIMPLEWALLET} )
target_link_libraries(simplewallet epee wallet rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES})
add_library(node_rpc_proxy ${NODE_RPC_PROXY})
target_link_libraries(simplewallet epee wallet transfers rpc cryptonote_core crypto common upnpc-static node_rpc_proxy serialization ${Boost_LIBRARIES})
add_library(logger ${LOGGER})
add_library(transfers ${TRANSFERS})


add_dependencies(connectivity_tool version)
add_dependencies(daemon version)
add_dependencies(rpc version)
add_dependencies(simplewallet version)

set_property(TARGET common crypto cryptonote_core rpc wallet node_rpc_proxy PROPERTY FOLDER "libs")
set_property(TARGET common crypto cryptonote_core rpc System wallet node_rpc_proxy serialization logger transfers inprocess_node PROPERTY FOLDER "libs")
set_property(TARGET daemon simplewallet connectivity_tool simpleminer PROPERTY FOLDER "prog")
set_property(TARGET daemon PROPERTY OUTPUT_NAME "bytecoind")
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
160 changes: 160 additions & 0 deletions src/Platform/Linux/System/Dispatcher.cpp
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();
}
}
64 changes: 64 additions & 0 deletions src/Platform/Linux/System/Dispatcher.h
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);
};

}
60 changes: 34 additions & 26 deletions src/System/Event.cpp → src/Platform/Linux/System/Event.cpp
100644 → 100755
Original file line number Diff line number Diff line change
@@ -17,28 +17,35 @@

#include "Event.h"
#include <cassert>
#include "System.h"
#include <iostream>
#include "Dispatcher.h"

struct Event::Waiter {
Event::Waiter* next;
using namespace System;

namespace {

struct Waiter {
Waiter* next;
void* context;
};

Event::Event() : system(nullptr) {
}

Event::Event(System& system) : system(&system), first(nullptr), state(false) {
Event::Event() : dispatcher(nullptr) {
}

Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) {
}

Event::Event(Event&& other) : system(other.system) {
if (other.system != nullptr) {
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.system = nullptr;
other.dispatcher = nullptr;
}
}

@@ -48,51 +55,52 @@ Event::~Event() {

Event& Event::operator=(Event&& other) {
assert(first == nullptr);
system = other.system;
if (other.system != nullptr) {
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}

state = other.state;
other.system = nullptr;
other.dispatcher = nullptr;
}

return *this;
}

bool Event::get() const {
assert(system != nullptr);
assert(dispatcher != nullptr);
return state;
}

void Event::clear() {
assert(system != nullptr);
assert(dispatcher != nullptr);
state = false;
}

void Event::set() {
assert(system != nullptr);
assert(dispatcher != nullptr);
state = true;
for (Waiter* waiter = first; waiter != nullptr; waiter = waiter->next) {
system->pushContext(waiter->context);
for (Waiter* waiter = static_cast<Waiter*>(first); waiter != nullptr; waiter = waiter->next) {
dispatcher->pushContext(waiter->context);
}

first = nullptr;
}

void Event::wait() {
assert(system != nullptr);
Waiter waiter = {nullptr, system->getCurrentContext()};
if (first != nullptr) {
last->next = &waiter;
} else {
first = &waiter;
}
assert(dispatcher != nullptr);
if (!state) {
Waiter waiter = { nullptr, dispatcher->getCurrentContext() };
if (first != nullptr) {
static_cast<Waiter*>(last)->next = &waiter;
} else {
first = &waiter;
}

last = &waiter;
while (!state) {
system->yield();
last = &waiter;
dispatcher->yield();
assert(dispatcher != nullptr);
}
}
16 changes: 9 additions & 7 deletions src/System/Event.h → src/Platform/Linux/System/Event.h
100644 → 100755
Original file line number Diff line number Diff line change
@@ -17,12 +17,14 @@

#pragma once

class System;
namespace System {

class Dispatcher;

class Event {
public:
Event();
explicit Event(System& system);
explicit Event(Dispatcher& dispatcher);
Event(const Event&) = delete;
Event(Event&& other);
~Event();
@@ -34,10 +36,10 @@ class Event {
void wait();

private:
struct Waiter;

System* system;
Waiter* first;
Waiter* last;
Dispatcher* dispatcher;
void* first;
void* last;
bool state;
};

}
Original file line number Diff line number Diff line change
@@ -15,5 +15,4 @@
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.


bool transactions_generation_from_blockchain(std::string& blockchain_path);
#include "InterruptedException.h"
10 changes: 5 additions & 5 deletions utils/test-static-assert.c → ...tform/Linux/System/InterruptedException.h
100644 → 100755
Original file line number Diff line number Diff line change
@@ -15,9 +15,9 @@
// 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 <assert.h>
#pragma once

static_assert(1, "FAIL");
int main(int argc, char *argv[]) {
return 0;
}
#include <exception>

class InterruptedException : public std::exception {
};
297 changes: 297 additions & 0 deletions src/Platform/Linux/System/TcpConnection.cpp
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");
}
}
14 changes: 10 additions & 4 deletions src/System/TcpConnection.h → src/Platform/Linux/System/TcpConnection.h
100644 → 100755
Original file line number Diff line number Diff line change
@@ -19,8 +19,11 @@

#include <cstddef>
#include <cstdint>
#include <stdint.h>

class System;
namespace System {

class Dispatcher;

class TcpConnection {
public:
@@ -39,9 +42,12 @@ class TcpConnection {
friend class TcpConnector;
friend class TcpListener;

explicit TcpConnection(System& system, void* socket);
explicit TcpConnection(Dispatcher& dispatcher, int socket);

System* system;
void* socket;
Dispatcher* dispatcher;
int connection;
bool stopped;
void* context;
};

}
213 changes: 213 additions & 0 deletions src/Platform/Linux/System/TcpConnector.cpp
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;
}
50 changes: 50 additions & 0 deletions src/Platform/Linux/System/TcpConnector.h
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;
};

}
207 changes: 207 additions & 0 deletions src/Platform/Linux/System/TcpListener.cpp
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;
}
49 changes: 49 additions & 0 deletions src/Platform/Linux/System/TcpListener.h
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;
};

}
160 changes: 160 additions & 0 deletions src/Platform/Linux/System/Timer.cpp
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;
}
46 changes: 46 additions & 0 deletions src/Platform/Linux/System/Timer.h
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;
};

}
169 changes: 169 additions & 0 deletions src/Platform/OSX/System/Dispatcher.cpp
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();
}
}
66 changes: 66 additions & 0 deletions src/Platform/OSX/System/Dispatcher.h
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);
};

}
106 changes: 106 additions & 0 deletions src/Platform/OSX/System/Event.cpp
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);
}
}
45 changes: 45 additions & 0 deletions src/Platform/OSX/System/Event.h
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;
};

}
18 changes: 18 additions & 0 deletions src/Platform/OSX/System/InterruptedException.cpp
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"
27 changes: 27 additions & 0 deletions src/Platform/OSX/System/InterruptedException.h
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 {
};

}
247 changes: 247 additions & 0 deletions src/Platform/OSX/System/TcpConnection.cpp
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;
}
53 changes: 53 additions & 0 deletions src/Platform/OSX/System/TcpConnection.h
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;
};

}
213 changes: 213 additions & 0 deletions src/Platform/OSX/System/TcpConnector.cpp
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");
}
18 changes: 13 additions & 5 deletions src/System/TcpConnector.h → src/Platform/OSX/System/TcpConnector.h
100644 → 100755
Original file line number Diff line number Diff line change
@@ -20,22 +20,30 @@
#include <cstdint>
#include <string>

class System;
namespace System {

class Dispatcher;
class TcpConnection;

class TcpConnector {
public:
TcpConnector();
TcpConnector(System& system, const std::string& address, uint16_t port);
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:
System* system;
std::string m_address;
uint16_t m_port;
Dispatcher* dispatcher;
std::string address;
uint16_t port;
bool stopped;
void* context;
};

}
202 changes: 202 additions & 0 deletions src/Platform/OSX/System/TcpListener.cpp
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;
}
Loading

0 comments on commit 1743402

Please sign in to comment.