Spawning new thread when we are reaching stack limit

This commit is contained in:
Tomusdrw 2016-01-26 10:15:55 +01:00
parent 8ad0a73fec
commit c66aa52166
7 changed files with 51 additions and 21 deletions

View File

@ -23,6 +23,7 @@ num_cpus = "0.2"
docopt = "0.6" docopt = "0.6"
docopt_macros = "0.6" docopt_macros = "0.6"
ctrlc = "1.0" ctrlc = "1.0"
crossbeam = "0.1.5"
clippy = "0.0.37" clippy = "0.0.37"
[features] [features]

View File

@ -64,7 +64,7 @@ impl IntoJit<evmjit::I256> for H256 {
for i in 0..self.bytes().len() { for i in 0..self.bytes().len() {
let rev = self.bytes().len() - 1 - i; let rev = self.bytes().len() - 1 - i;
let pos = rev / 8; let pos = rev / 8;
ret[pos] += (self.bytes()[i] as u64) << (rev % 8) * 8; ret[pos] += (self.bytes()[i] as u64) << ((rev % 8) * 8);
} }
evmjit::I256 { words: ret } evmjit::I256 { words: ret }
} }
@ -218,9 +218,11 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
} }
} }
match self.ext.call(&call_gas, match self.ext.call(
&call_gas,
&self.address,
&receive_address, &receive_address,
&value, Some(value),
unsafe { slice::from_raw_parts(in_beg, in_size as usize) }, unsafe { slice::from_raw_parts(in_beg, in_size as usize) },
&code_address, &code_address,
unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }) { unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }) {
@ -262,7 +264,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
} }
let bytes_ref: &[u8] = slice::from_raw_parts(beg, size as usize); let bytes_ref: &[u8] = slice::from_raw_parts(beg, size as usize);
self.ext.log(topics, bytes_ref.to_vec()); self.ext.log(topics, bytes_ref);
} }
} }
@ -287,8 +289,8 @@ impl evm::Evm for JitEvm {
assert!(params.gas <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63"); assert!(params.gas <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63");
assert!(params.gas_price <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63"); assert!(params.gas_price <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63");
let call_data = params.data.unwrap_or(vec![]); let call_data = params.data.unwrap_or_else(Vec::new);
let code = params.code.unwrap_or(vec![]); let code = params.code.unwrap_or_else(Vec::new);
let mut data = evmjit::RuntimeDataHandle::new(); let mut data = evmjit::RuntimeDataHandle::new();
data.gas = params.gas.low_u64() as i64; data.gas = params.gas.low_u64() as i64;
@ -303,7 +305,10 @@ impl evm::Evm for JitEvm {
data.address = params.address.into_jit(); data.address = params.address.into_jit();
data.caller = params.sender.into_jit(); data.caller = params.sender.into_jit();
data.origin = params.origin.into_jit(); data.origin = params.origin.into_jit();
data.call_value = params.value.into_jit(); data.call_value = match params.value {
ActionValue::Transfer(val) => val.into_jit(),
ActionValue::Apparent(val) => val.into_jit()
};
data.author = ext.env_info().author.clone().into_jit(); data.author = ext.env_info().author.clone().into_jit();
data.difficulty = ext.env_info().difficulty.into_jit(); data.difficulty = ext.env_info().difficulty.into_jit();

View File

@ -215,6 +215,7 @@ fn test_origin(factory: super::Factory) {
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap()); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap());
} }
// TODO [todr] Fails with Signal 11 on JIT
evm_test!{test_sender: test_sender_jit, test_sender_int} evm_test!{test_sender: test_sender_jit, test_sender_int}
fn test_sender(factory: super::Factory) { fn test_sender(factory: super::Factory) {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();

View File

@ -5,6 +5,12 @@ use engine::*;
use evm::{self, Ext}; use evm::{self, Ext};
use externalities::*; use externalities::*;
use substate::*; use substate::*;
use crossbeam;
/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM)
/// TODO [todr] We probably need some more sophisticated calculations here (limit on my machine 132)
/// Maybe something like here: https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp
const MAX_VM_DEPTH_FOR_THREAD: usize = 128;
/// Returns new address created from address and given nonce. /// Returns new address created from address and given nonce.
pub fn contract_address(address: &Address, nonce: &U256) -> Address { pub fn contract_address(address: &Address, nonce: &U256) -> Address {
@ -161,12 +167,32 @@ impl<'a> Executive<'a> {
Ok(try!(self.finalize(t, substate, res))) Ok(try!(self.finalize(t, substate, res)))
} }
fn exec_vm(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy) -> evm::Result {
// Ordinary execution - keep VM in same thread
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy);
let vm_factory = self.engine.vm_factory();
return vm_factory.create().exec(params, &mut ext);
}
// Start in new thread to reset stack
// TODO [todr] No thread builder yet, so we need to reset once for a while
// https://github.com/aturon/crossbeam/issues/16
crossbeam::scope(|scope| {
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy);
let vm_factory = self.engine.vm_factory();
scope.spawn(move || {
vm_factory.create().exec(params, &mut ext)
})
}).join()
}
/// Calls contract function with given contract params. /// Calls contract function with given contract params.
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate and the output. /// Modifies the substate and the output.
/// Returns either gas_left or `evm::Error`. /// Returns either gas_left or `evm::Error`.
pub fn call(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result { pub fn call(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result {
println!("Calling executive. Sender: {}", params.sender);
// backup used in case of running out of gas // backup used in case of running out of gas
let backup = self.state.clone(); let backup = self.state.clone();
@ -201,8 +227,7 @@ impl<'a> Executive<'a> {
let mut unconfirmed_substate = Substate::new(); let mut unconfirmed_substate = Substate::new();
let res = { let res = {
let mut ext = self.as_externalities(OriginInfo::from(&params), &mut unconfirmed_substate, OutputPolicy::Return(output)); self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output))
self.engine.vm_factory().create().exec(params, &mut ext)
}; };
trace!("exec: sstore-clears={}\n", unconfirmed_substate.sstore_clears_count); trace!("exec: sstore-clears={}\n", unconfirmed_substate.sstore_clears_count);
@ -235,8 +260,7 @@ impl<'a> Executive<'a> {
} }
let res = { let res = {
let mut ext = self.as_externalities(OriginInfo::from(&params), &mut unconfirmed_substate, OutputPolicy::InitContract); self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract)
self.engine.vm_factory().create().exec(params, &mut ext)
}; };
self.enact_result(&res, substate, unconfirmed_substate, backup); self.enact_result(&res, substate, unconfirmed_substate, backup);
res res
@ -277,7 +301,6 @@ impl<'a> Executive<'a> {
match result { match result {
Err(evm::Error::Internal) => Err(ExecutionError::Internal), Err(evm::Error::Internal) => Err(ExecutionError::Internal),
// TODO [ToDr] BadJumpDestination @debris - how to handle that?
Err(_) => { Err(_) => {
Ok(Executed { Ok(Executed {
gas: t.gas, gas: t.gas,
@ -302,7 +325,6 @@ impl<'a> Executive<'a> {
} }
fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate, backup: State) { fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate, backup: State) {
// TODO: handle other evm::Errors same as OutOfGas once they are implemented
match *result { match *result {
Err(evm::Error::OutOfGas) Err(evm::Error::OutOfGas)
| Err(evm::Error::BadJumpDestination {..}) | Err(evm::Error::BadJumpDestination {..})

View File

@ -90,6 +90,7 @@ extern crate num_cpus;
extern crate evmjit; extern crate evmjit;
#[macro_use] #[macro_use]
extern crate ethcore_util as util; extern crate ethcore_util as util;
extern crate crossbeam;
// NOTE: Add doc parser exception for these pub declarations. // NOTE: Add doc parser exception for these pub declarations.

View File

@ -271,8 +271,8 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"} declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"}
declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"} declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"}
// this one crashes with some vm internal error. Separately they pass. declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"}
declare_test!{ignore => ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"} // TODO [todr] Fails with Signal 11 when using JIT
declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"} declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"}
declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"} declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"}
declare_test!{heavy => ExecutiveTests_vmInputLimits, "VMTests/vmInputLimits"} declare_test!{heavy => ExecutiveTests_vmInputLimits, "VMTests/vmInputLimits"}

View File

@ -73,7 +73,7 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
declare_test!{StateTests_stBlockHashTest, "StateTests/stBlockHashTest"} declare_test!{StateTests_stBlockHashTest, "StateTests/stBlockHashTest"}
declare_test!{StateTests_stCallCodes, "StateTests/stCallCodes"} declare_test!{StateTests_stCallCodes, "StateTests/stCallCodes"}
declare_test!{ignore => StateTests_stCallCreateCallCodeTest, "StateTests/stCallCreateCallCodeTest"} //<< Out of stack declare_test!{StateTests_stCallCreateCallCodeTest, "StateTests/stCallCreateCallCodeTest"}
declare_test!{StateTests_stDelegatecallTest, "StateTests/stDelegatecallTest"} declare_test!{StateTests_stDelegatecallTest, "StateTests/stDelegatecallTest"}
declare_test!{StateTests_stExample, "StateTests/stExample"} declare_test!{StateTests_stExample, "StateTests/stExample"}
declare_test!{StateTests_stInitCodeTest, "StateTests/stInitCodeTest"} declare_test!{StateTests_stInitCodeTest, "StateTests/stInitCodeTest"}
@ -81,12 +81,12 @@ declare_test!{StateTests_stLogTests, "StateTests/stLogTests"}
declare_test!{heavy => StateTests_stMemoryStressTest, "StateTests/stMemoryStressTest"} declare_test!{heavy => StateTests_stMemoryStressTest, "StateTests/stMemoryStressTest"}
declare_test!{heavy => StateTests_stMemoryTest, "StateTests/stMemoryTest"} declare_test!{heavy => StateTests_stMemoryTest, "StateTests/stMemoryTest"}
declare_test!{StateTests_stPreCompiledContracts, "StateTests/stPreCompiledContracts"} declare_test!{StateTests_stPreCompiledContracts, "StateTests/stPreCompiledContracts"}
declare_test!{heavy => StateTests_stQuadraticComplexityTest, "StateTests/stQuadraticComplexityTest"} //<< Too long declare_test!{heavy => StateTests_stQuadraticComplexityTest, "StateTests/stQuadraticComplexityTest"}
declare_test!{ignore => StateTests_stRecursiveCreate, "StateTests/stRecursiveCreate"} //<< Out of stack declare_test!{StateTests_stRecursiveCreate, "StateTests/stRecursiveCreate"}
declare_test!{StateTests_stRefundTest, "StateTests/stRefundTest"} declare_test!{StateTests_stRefundTest, "StateTests/stRefundTest"}
declare_test!{StateTests_stSolidityTest, "StateTests/stSolidityTest"} declare_test!{StateTests_stSolidityTest, "StateTests/stSolidityTest"}
declare_test!{ignore => StateTests_stSpecialTest, "StateTests/stSpecialTest"} //<< Out of Stack declare_test!{StateTests_stSpecialTest, "StateTests/stSpecialTest"}
declare_test!{ignore => StateTests_stSystemOperationsTest, "StateTests/stSystemOperationsTest"} //<< Out of stack declare_test!{StateTests_stSystemOperationsTest, "StateTests/stSystemOperationsTest"}
declare_test!{StateTests_stTransactionTest, "StateTests/stTransactionTest"} declare_test!{StateTests_stTransactionTest, "StateTests/stTransactionTest"}
declare_test!{StateTests_stTransitionTest, "StateTests/stTransitionTest"} declare_test!{StateTests_stTransitionTest, "StateTests/stTransitionTest"}
declare_test!{StateTests_stWalletTest, "StateTests/stWalletTest"} declare_test!{StateTests_stWalletTest, "StateTests/stWalletTest"}