Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Adjustable stack size for EVM #2483

Merged
merged 5 commits into from
Oct 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions ethcore/src/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, E
use crossbeam;
pub use types::executed::{Executed, ExecutionResult};

/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM)
/// Roughly estimate what stack size each level of evm depth will use
/// 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 = 64;
const STACK_SIZE_PER_DEPTH: usize = 24*1024;

/// Returns new address created from address and given nonce.
pub fn contract_address(address: &Address, nonce: &U256) -> Address {
Expand Down Expand Up @@ -149,12 +149,13 @@ impl<'a> Executive<'a> {

// TODO: we might need bigints here, or at least check overflows.
let balance = self.state.balance(&sender);
let gas_cost = U512::from(t.gas) * U512::from(t.gas_price);
let gas_cost = t.gas.full_mul(t.gas_price);
let total_cost = U512::from(t.value) + gas_cost;

// avoid unaffordable transactions
if U512::from(balance) < total_cost {
return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: U512::from(balance) }));
let balance512 = U512::from(balance);
if balance512 < total_cost {
return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: balance512 }));
}

// NOTE: there can be no invalid transactions from this point.
Expand Down Expand Up @@ -212,8 +213,11 @@ impl<'a> Executive<'a> {
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result<U256> where T: Tracer, V: VMTracer {

let depth_threshold = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get() / STACK_SIZE_PER_DEPTH);

// Ordinary execution - keep VM in same thread
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
if (self.depth + 1) % depth_threshold != 0 {
let vm_factory = self.vm_factory;
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
Expand Down Expand Up @@ -413,7 +417,7 @@ impl<'a> Executive<'a> {

// real ammount to refund
let gas_left_prerefund = match result { Ok(x) => x, _ => 0.into() };
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) / U256::from(2));
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1);
let gas_left = gas_left_prerefund + refunded;

let gas_used = t.gas - gas_left;
Expand Down
2 changes: 2 additions & 0 deletions util/io/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ mod panics;
use mio::{EventLoop, Token};
use std::fmt;

pub use worker::LOCAL_STACK_SIZE;

#[derive(Debug)]
/// IO Error
pub enum IoError {
Expand Down
13 changes: 12 additions & 1 deletion util/io/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,19 @@ use crossbeam::sync::chase_lev;
use service::{HandlerId, IoChannel, IoContext};
use IoHandler;
use panics::*;
use std::cell::Cell;

use std::sync::{Condvar as SCondvar, Mutex as SMutex};

const STACK_SIZE: usize = 16*1024*1024;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like with 8kb frame the stack of 8mb should be enough

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might be so, but previously we used 32kb
see above


thread_local! {
/// Stack size
/// Should be modified if it is changed in Rust since it is no way
/// to know or get it
pub static LOCAL_STACK_SIZE: Cell<usize> = Cell::new(::std::env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok()).unwrap_or(2 * 1024 * 1024));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimum should be 512k, that's the default on OS X

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

512k must be default for the main thread only

this expression exactly the same value which rust uses for spawned threads:
https://github.com/rust-lang/rust/blob/master/src/libstd/sys/common/util.rs#L18

}

pub enum WorkType<Message> {
Readable,
Writable,
Expand Down Expand Up @@ -66,8 +76,9 @@ impl Worker {
deleting: deleting.clone(),
wait_mutex: wait_mutex.clone(),
};
worker.thread = Some(thread::Builder::new().name(format!("IO Worker #{}", index)).spawn(
worker.thread = Some(thread::Builder::new().stack_size(STACK_SIZE).name(format!("IO Worker #{}", index)).spawn(
move || {
LOCAL_STACK_SIZE.with(|val| val.set(STACK_SIZE));
panic_handler.catch_panic(move || {
Worker::work_loop(stealer, channel.clone(), wait, wait_mutex.clone(), deleting)
}).unwrap()
Expand Down