|
|
@ -20,18 +20,69 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
#include "ExtVM.h" |
|
|
|
|
|
|
|
#include <exception> |
|
|
|
#include <boost/thread.hpp> |
|
|
|
#include "Executive.h" |
|
|
|
using namespace std; |
|
|
|
|
|
|
|
using namespace dev; |
|
|
|
using namespace dev::eth; |
|
|
|
|
|
|
|
namespace |
|
|
|
{ |
|
|
|
|
|
|
|
static unsigned const c_depthLimit = 1024; |
|
|
|
|
|
|
|
/// Upper bound of stack space needed by single CALL/CREATE execution. Set experimentally.
|
|
|
|
static size_t const c_singleExecutionStackSize = 12 * 1024; |
|
|
|
|
|
|
|
/// Standard OSX thread stack limit. Should be reasonable for other platforms too.
|
|
|
|
static size_t const c_defaultStackSize = 512 * 1024; |
|
|
|
|
|
|
|
/// On what depth execution should be offloaded to additional separated stack space.
|
|
|
|
static unsigned const c_offloadPoint = c_defaultStackSize / c_singleExecutionStackSize; |
|
|
|
|
|
|
|
void goOnOffloadedStack(Executive& _e, OnOpFunc const& _onOp) |
|
|
|
{ |
|
|
|
// Set new stack size enouth to handle the rest of the calls up to the limit.
|
|
|
|
boost::thread::attributes attrs; |
|
|
|
attrs.set_stack_size((c_depthLimit - c_offloadPoint) * c_singleExecutionStackSize); |
|
|
|
|
|
|
|
// Create new thread with big stack and join immediately.
|
|
|
|
// TODO: It is possible to switch the implementation to Boost.Context or similar when the API is stable.
|
|
|
|
std::exception_ptr exception; |
|
|
|
boost::thread{attrs, [&]{ |
|
|
|
try |
|
|
|
{ |
|
|
|
_e.go(_onOp); |
|
|
|
} |
|
|
|
catch (...) |
|
|
|
{ |
|
|
|
exception = std::current_exception(); // Catch all exceptions to be rethrown in parent thread.
|
|
|
|
} |
|
|
|
}}.join(); |
|
|
|
if (exception) |
|
|
|
std::rethrow_exception(exception); |
|
|
|
} |
|
|
|
|
|
|
|
void go(unsigned _depth, Executive& _e, OnOpFunc const& _onOp) |
|
|
|
{ |
|
|
|
// If in the offloading point we need to switch to additional separated stack space.
|
|
|
|
// Current stack is too small to handle more CALL/CREATE executions.
|
|
|
|
// It needs to be done only once as newly allocated stack space it enough to handle
|
|
|
|
// the rest of the calls up to the depth limit (c_depthLimit).
|
|
|
|
if (_depth == c_offloadPoint) |
|
|
|
goOnOffloadedStack(_e, _onOp); |
|
|
|
else |
|
|
|
_e.go(_onOp); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool ExtVM::call(CallParameters& _p) |
|
|
|
{ |
|
|
|
Executive e(m_s, lastHashes, depth + 1); |
|
|
|
if (!e.call(_p, gasPrice, origin)) |
|
|
|
{ |
|
|
|
e.go(_p.onOp); |
|
|
|
go(depth, e, _p.onOp); |
|
|
|
e.accrueSubState(sub); |
|
|
|
} |
|
|
|
_p.gas = e.gas(); |
|
|
@ -47,7 +98,7 @@ h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc |
|
|
|
Executive e(m_s, lastHashes, depth + 1); |
|
|
|
if (!e.create(myAddress, _endowment, gasPrice, io_gas, _code, origin)) |
|
|
|
{ |
|
|
|
e.go(_onOp); |
|
|
|
go(depth, e, _onOp); |
|
|
|
e.accrueSubState(sub); |
|
|
|
} |
|
|
|
io_gas = e.gas(); |
|
|
|