Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
YottaChain
YTBP
提交
cf96345f
Y
YTBP
项目概览
YottaChain
/
YTBP
通知
0
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Y
YTBP
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
cf96345f
编写于
11月 17, 2017
作者:
B
Bart Wyatt
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'feature/wasm-cache' into eos-noon
上级
635abdb6
08fb7787
变更
7
显示空白变更内容
内联
并排
Showing
7 changed file
with
450 addition
and
158 deletion
+450
-158
libraries/chain/apply_context.cpp
libraries/chain/apply_context.cpp
+11
-1
libraries/chain/contracts/eosio_contract.cpp
libraries/chain/contracts/eosio_contract.cpp
+8
-2
libraries/chain/include/eosio/chain/chain_controller.hpp
libraries/chain/include/eosio/chain/chain_controller.hpp
+7
-2
libraries/chain/include/eosio/chain/exceptions.hpp
libraries/chain/include/eosio/chain/exceptions.hpp
+1
-0
libraries/chain/include/eosio/chain/wasm_interface.hpp
libraries/chain/include/eosio/chain/wasm_interface.hpp
+78
-19
libraries/chain/include/eosio/chain/wasm_interface_private.hpp
...ries/chain/include/eosio/chain/wasm_interface_private.hpp
+23
-20
libraries/chain/wasm_interface.cpp
libraries/chain/wasm_interface.cpp
+322
-114
未找到文件。
libraries/chain/apply_context.cpp
浏览文件 @
cf96345f
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/chain_controller.hpp>
#include <eosio/chain/wasm_interface.hpp>
namespace
eosio
{
namespace
chain
{
void
apply_context
::
exec
()
...
...
@@ -8,7 +9,16 @@ void apply_context::exec()
if
(
native
)
{
(
*
native
)(
*
this
);
}
else
{
wlog
(
"WASM handler not yet implemented ${receiver} ${scope}::${name}"
,
(
"receiver"
,
receiver
)(
"scope"
,
act
.
scope
)(
"name"
,
act
.
name
)
);
const
auto
&
a
=
mutable_controller
.
get_database
().
get
<
account_object
,
by_name
>
(
receiver
);
if
(
a
.
code
.
size
()
>
0
)
{
// get code from cache
auto
code
=
mutable_controller
.
get_wasm_cache
().
checkout_scoped
(
a
.
code_version
,
a
.
code
.
data
(),
a
.
code
.
size
());
// get wasm_interface
auto
&
wasm
=
wasm_interface
::
get
();
wasm
.
apply
(
code
,
*
this
);
}
}
}
/// exec()
...
...
libraries/chain/contracts/eosio_contract.cpp
浏览文件 @
cf96345f
...
...
@@ -191,19 +191,25 @@ void apply_eosio_setcode(apply_context& context) {
FC_ASSERT
(
act
.
vmtype
==
0
);
FC_ASSERT
(
act
.
vmversion
==
0
);
auto
code_id
=
fc
::
sha256
::
hash
(
act
.
code
.
data
(),
act
.
code
.
size
()
);
const
auto
&
account
=
db
.
get
<
account_object
,
by_name
>
(
act
.
account
);
// wlog( "set code: ${size}", ("size",act.code.size()));
db
.
modify
(
account
,
[
&
](
auto
&
a
)
{
/** TODO: consider whether a microsecond level local timestamp is sufficient to detect code version changes*/
#warning TODO: update setcode message to include the hash, then validate it in validate
a
.
code_version
=
fc
::
sha256
::
hash
(
act
.
code
.
data
(),
act
.
code
.
size
()
)
;
a
.
code_version
=
code_id
;
a
.
code
.
resize
(
act
.
code
.
size
()
);
memcpy
(
a
.
code
.
data
(),
act
.
code
.
data
(),
act
.
code
.
size
()
);
});
// create an apply context for initialization
apply_context
init_context
(
context
.
mutable_controller
,
context
.
mutable_db
,
context
.
trx
,
context
.
act
,
act
.
account
);
wasm_interface
::
get
().
init
(
init_context
);
// get code from cache
auto
code
=
context
.
mutable_controller
.
get_wasm_cache
().
checkout_scoped
(
code_id
,
act
.
code
.
data
(),
act
.
code
.
size
());
wasm_interface
::
get
().
init
(
code
,
init_context
);
}
void
apply_eosio_setabi
(
apply_context
&
context
)
{
...
...
libraries/chain/include/eosio/chain/chain_controller.hpp
浏览文件 @
cf96345f
...
...
@@ -18,6 +18,7 @@
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/chain/contracts/genesis_state.hpp>
#include <eosio/chain/wasm_interface.hpp>
#include <fc/log/logger.hpp>
...
...
@@ -27,6 +28,7 @@ namespace eosio { namespace chain {
using
database
=
chainbase
::
database
;
using
boost
::
signals2
::
signal
;
namespace
contracts
{
class
chain_initializer
;
}
enum
validation_steps
...
...
@@ -269,8 +271,9 @@ namespace eosio { namespace chain {
const
deque
<
signed_transaction
>&
pending
()
const
{
return
_pending_transactions
;
}
wasm_cache
&
get_wasm_cache
()
{
return
_wasm_cache
;
}
private:
const
apply_handler
*
find_apply_handler
(
account_name
contract
,
scope_name
scope
,
action_name
act
)
const
;
...
...
@@ -396,6 +399,8 @@ namespace eosio { namespace chain {
typedef
pair
<
scope_name
,
action_name
>
handler_key
;
map
<
account_name
,
map
<
handler_key
,
apply_handler
>
>
_apply_handlers
;
wasm_cache
_wasm_cache
;
};
}
}
libraries/chain/include/eosio/chain/exceptions.hpp
浏览文件 @
cf96345f
...
...
@@ -43,6 +43,7 @@ namespace eosio { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION
(
tx_msgs_code_exceeded
,
eosio
::
chain
::
transaction_exception
,
3030019
,
"Number of transaction messages per code account has been exceeded"
)
FC_DECLARE_DERIVED_EXCEPTION
(
tx_missing_read_scope
,
eosio
::
chain
::
transaction_exception
,
3030020
,
"missing required read scope"
)
FC_DECLARE_DERIVED_EXCEPTION
(
tx_missing_write_scope
,
eosio
::
chain
::
transaction_exception
,
3030021
,
"missing required write scope"
)
FC_DECLARE_DERIVED_EXCEPTION
(
wasm_execution_error
,
eosio
::
chain
::
transaction_exception
,
3030022
,
"Runtime Error Processing WASM"
)
FC_DECLARE_DERIVED_EXCEPTION
(
invalid_pts_address
,
eosio
::
chain
::
utility_exception
,
3060001
,
"invalid pts address"
)
FC_DECLARE_DERIVED_EXCEPTION
(
insufficient_feeds
,
eosio
::
chain
::
chain_exception
,
37006
,
"insufficient feeds"
)
...
...
libraries/chain/include/eosio/chain/wasm_interface.hpp
浏览文件 @
cf96345f
...
...
@@ -12,33 +12,93 @@ namespace eosio { namespace chain {
class
intrinsics_accessor
;
/**
* @class wasm_interface
*
* EOS.IO uses the wasm-jit library to evaluate web assembly code. This library relies
* upon a singlton thread-local interface which means there can be only one instance
* per thread.
* @class wasm_cache
*
* This class manages compilation, re-use and memory sanitation for WASM contracts
* As the same code can be running on many threads in parallel, some contracts will have multiple
* copies.
*/
class
wasm_
interfac
e
{
class
wasm_
cach
e
{
public:
static
wasm_interface
&
get
();
wasm_cache
();
~
wasm_cache
();
Runtime
::
MemoryInstance
*
memory
()
const
;
uint32_t
memory_size
()
const
;
/**
* an opaque code entry used in the wasm_interface class
*/
struct
entry
;
/**
* Will initalize the given code or used a cached version if one exists for
* the given codeid.
* Checkout the desired code from the cache. Code is idenfied via a digest of the wasm binary
*
* @param wasmcode - code in wasm format
* @param code_id - a digest of the wasm_binary bytes
* @param wasm_binary - a pointer to the wasm_binary bytes
* @param wasm_binary_size - the size of the wasm_binary bytes array
* @return an entry which can be immediately used by the wasm_interface to execute contract code
*/
void
load
(
digest_type
codeid
,
const
char
*
wasmcode
,
size_t
code
size
);
entry
&
checkout
(
const
digest_type
&
code_id
,
const
char
*
wasm_binary
,
size_t
wasm_binary_
size
);
/**
* If there exists a cached version of the code for codeid then it is released
* Return an entry to the cache so that future checkouts may retrieve it
*
* @param code_id - a digest of the wasm_binary bytes
* @param code - the entry which should be considered invalid post-call
*/
void
unload
(
digest_type
codeid
);
void
checkin
(
const
digest_type
&
code_id
,
entry
&
code
);
/**
* RAII wrapper to make sure that the cache entries are returned regardless of exceptions etc
*/
struct
scoped_entry
{
explicit
scoped_entry
(
const
digest_type
&
code_id
,
entry
&
code
,
wasm_cache
&
cache
)
:
code_id
(
code_id
)
,
code
(
code
)
,
cache
(
cache
)
{}
~
scoped_entry
()
{
cache
.
checkin
(
code_id
,
code
);
}
operator
entry
&
()
{
return
code
;
}
digest_type
code_id
;
entry
&
code
;
wasm_cache
&
cache
;
};
/**
* Checkout the desired code from the cache. Code is idenfied via a digest of the wasm binary
* this method will wrap the code in an RAII construct so that it will automatically
* return to the cache when it falls out of scope
*
* @param code_id - a digest of the wasm_binary bytes
* @param wasm_binary - a pointer to the wasm_binary bytes
* @param wasm_binary_size - the size of the wasm_binary bytes array
* @return an entry which can be immediately used by the wasm_interface to execute contract code
*/
scoped_entry
checkout_scoped
(
const
digest_type
&
code_id
,
const
char
*
wasm_binary
,
size_t
wasm_binary_size
)
{
return
scoped_entry
(
code_id
,
checkout
(
code_id
,
wasm_binary
,
wasm_binary_size
),
*
this
);
}
private:
unique_ptr
<
struct
wasm_cache_impl
>
_my
;
};
/**
* @class wasm_interface
*
* EOS.IO uses the wasm-jit library to evaluate web assembly code. This library relies
* upon a singlton thread-local interface which means there can be only one instance
* per thread.
*
*/
class
wasm_interface
{
public:
static
wasm_interface
&
get
();
/**
* Calls the init() method on the currently loaded code
...
...
@@ -46,7 +106,7 @@ namespace eosio { namespace chain {
* @param context - the interface by which the contract can interact
* with blockchain state.
*/
void
init
(
apply_context
&
context
);
void
init
(
wasm_cache
::
entry
&
code
,
apply_context
&
context
);
/**
* Calls the apply() method on the currently loaded code
...
...
@@ -54,11 +114,11 @@ namespace eosio { namespace chain {
* @param context - the interface by which the contract can interact
* with blockchain state.
*/
void
apply
(
apply_context
&
context
);
void
apply
(
wasm_cache
::
entry
&
code
,
apply_context
&
context
);
/**
*/
void
error
(
apply_context
&
context
);
void
error
(
wasm_cache
::
entry
&
code
,
apply_context
&
context
);
private:
wasm_interface
();
...
...
@@ -66,5 +126,4 @@ namespace eosio { namespace chain {
friend
class
eosio
::
chain
::
intrinsics_accessor
;
};
}
}
// eosio::chain
libraries/chain/include/eosio/chain/wasm_interface_private.hpp
浏览文件 @
cf96345f
#pragma once
#include <eosio/chain/wasm_interface.hpp>
#include <fc/optional.hpp>
#include "Runtime/Runtime.h"
#include "IR/Types.h"
...
...
@@ -9,31 +10,33 @@ namespace eosio { namespace chain {
using
namespace
IR
;
using
namespace
Runtime
;
using
namespace
fc
;
struct
module_state
{
ModuleInstance
*
instance
=
nullptr
;
Module
*
module
=
nullptr
;
apply_context
*
context
=
nullptr
;
int
mem_start
=
0
;
int
mem_end
=
1
<<
16
;
vector
<
char
>
init_memory
;
fc
::
sha256
code_version
;
struct
wasm_cache
::
entry
{
entry
(
ModuleInstance
*
instance
,
Module
*
module
)
:
instance
(
instance
)
,
module
(
module
)
{}
ModuleInstance
*
instance
;
Module
*
module
;
};
struct
wasm_
interface_impl
{
map
<
digest_type
,
module_state
>
code_cach
e
;
module_state
*
current_state
=
nullptr
;
struct
wasm_
context
{
wasm_cache
::
entry
&
cod
e
;
apply_context
&
context
;
};
wasm_interface
::
wasm_interface
()
:
my
(
new
wasm_interface_impl
()
)
{
}
struct
wasm_interface_impl
{
optional
<
wasm_context
>
current_context
;
void
call
(
const
string
&
entry_point
,
const
vector
<
Value
>&
args
,
wasm_cache
::
entry
&
code
,
apply_context
&
context
);
};
class
intrinsics_accessor
{
public:
static
module_state
&
get_module_state
(
wasm_interface
&
wasm
)
{
FC_ASSERT
(
wasm
.
my
->
current_
state
!=
nullptr
);
return
*
wasm
.
my
->
current_
state
;
static
wasm_context
&
get_context
(
wasm_interface
&
wasm
)
{
FC_ASSERT
(
wasm
.
my
->
current_
context
.
valid
()
);
return
*
wasm
.
my
->
current_
context
;
}
};
...
...
@@ -213,7 +216,7 @@ struct intrinsic_invoker_impl<Ret, std::tuple<array_ptr<T>, size_t, Inputs...>,
template
<
then_type
Then
>
static
Ret
translate_one
(
wasm_interface
&
wasm
,
Inputs
...
rest
,
Translated
...
translated
,
I32
ptr
,
I32
size
)
{
auto
mem
=
getDefaultMemory
(
intrinsics_accessor
::
get_
module_state
(
wasm
)
.
instance
);
auto
mem
=
getDefaultMemory
(
intrinsics_accessor
::
get_
context
(
wasm
).
code
.
instance
);
size_t
length
=
size_t
(
size
);
T
*
base
=
memoryArrayPtr
<
T
>
(
mem
,
ptr
,
length
);
return
Then
(
wasm
,
array_ptr
<
T
>
{
base
},
length
,
rest
...,
translated
...);
...
...
@@ -242,7 +245,7 @@ struct intrinsic_invoker_impl<Ret, std::tuple<T*, Inputs...>, std::tuple<Transla
template
<
then_type
Then
>
static
Ret
translate_one
(
wasm_interface
&
wasm
,
Inputs
...
rest
,
Translated
...
translated
,
I32
ptr
)
{
auto
mem
=
getDefaultMemory
(
intrinsics_accessor
::
get_
module_state
(
wasm
)
.
instance
);
auto
mem
=
getDefaultMemory
(
intrinsics_accessor
::
get_
context
(
wasm
).
code
.
instance
);
T
*
base
=
memoryArrayPtr
<
T
>
(
mem
,
ptr
,
1
);
return
Then
(
wasm
,
base
,
rest
...,
translated
...);
};
...
...
@@ -272,7 +275,7 @@ struct intrinsic_invoker_impl<Ret, std::tuple<T&, Inputs...>, std::tuple<Transla
static
Ret
translate_one
(
wasm_interface
&
wasm
,
Inputs
...
rest
,
Translated
...
translated
,
I32
ptr
)
{
// references cannot be created for null pointers
FC_ASSERT
(
ptr
!=
0
);
auto
mem
=
getDefaultMemory
(
intrinsics_accessor
::
get_
module_state
(
wasm
)
.
instance
);
auto
mem
=
getDefaultMemory
(
intrinsics_accessor
::
get_
context
(
wasm
).
code
.
instance
);
T
&
base
=
memoryRef
<
T
>
(
mem
,
ptr
);
return
Then
(
wasm
,
base
,
rest
...,
translated
...);
}
...
...
libraries/chain/wasm_interface.cpp
浏览文件 @
cf96345f
#include <eosio/chain/wasm_interface.hpp>
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/exceptions.hpp>
#include <boost/core/ignore_unused.hpp>
#include <eosio/chain/wasm_interface_private.hpp>
#include <fc/exception/exception.hpp>
...
...
@@ -16,11 +17,17 @@
#include "IR/Validate.h"
#include "IR/Types.h"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <mutex>
#include <thread>
#include <condition_variable>
using
namespace
IR
;
using
namespace
Runtime
;
using
boost
::
asio
::
io_service
;
namespace
eosio
{
namespace
chain
{
...
...
@@ -46,137 +53,338 @@ namespace eosio { namespace chain {
}
};
/**
* Implementation class for the wasm cache
* it is responsible for compiling and storing instances of wasm code for use
*
*/
struct
wasm_cache_impl
{
wasm_cache_impl
()
:
_ios
()
,
_work
(
_ios
)
{
_utility_thread
=
std
::
thread
([](
io_service
*
ios
){
ios
->
run
();
},
&
_ios
);
}
/**
* this must wait for all work to be done otherwise it may destroy memory
* referenced by other threads
*
* Expectations on wasm_cache dictate that all available code has been
* returned before this can be destroyed
*/
~
wasm_cache_impl
()
{
_work
.
reset
();
_ios
.
stop
();
_utility_thread
.
join
();
freeUnreferencedObjects
({});
}
std
::
mutex
global_load_mutex
;
wasm_interface
&
wasm_interface
::
get
()
{
static
bool
init_once
=
[](){
Runtime
::
init
();
return
true
;
}();
boost
::
ignore_unused
(
init_once
);
/**
* internal tracking structure which deduplicates memory images
* and tracks available vs in-use entries.
*
* The instances array has two sections, "available" instances
* are in the front of the vector and anything at an index of
* available_instances or greater is considered "in use"
*
* instances are stored as pointers so that their positions
* in the array can be moved without invaliding references to
* the instance handed out to other threads
*/
struct
code_info
{
// a clean image of the memory used to sanitize things on checkin
size_t
mem_start
=
0
;
size_t
mem_end
=
1
<<
16
;
vector
<
char
>
mem_image
;
// all existing instances of this code
vector
<
unique_ptr
<
wasm_cache
::
entry
>>
instances
;
size_t
available_instances
=
0
;
};
thread_local
wasm_interface
*
single
=
nullptr
;
if
(
!
single
)
{
single
=
new
wasm_interface
();
}
return
*
single
;
using
optional_info_ref
=
optional
<
std
::
reference_wrapper
<
code_info
>>
;
using
optional_entry_ref
=
optional
<
std
::
reference_wrapper
<
wasm_cache
::
entry
>>
;
/**
* Convenience method for running code with the _cache_lock and releaseint that lock
* when the code completes
*
* @param f - lambda to execute
* @return - varies depending on the signature of the lambda
*/
template
<
typename
F
>
auto
with_lock
(
F
f
)
{
std
::
lock_guard
<
std
::
mutex
>
lock
(
_cache_lock
);
return
f
();
};
/**
* Fetch the tracking struct given a code_id if it exists
*
* @param code_id
* @return
*/
optional_info_ref
fetch_info
(
const
digest_type
&
code_id
)
{
return
with_lock
([
&
,
this
](){
auto
iter
=
_cache
.
find
(
code_id
);
if
(
iter
!=
_cache
.
end
())
{
return
optional_info_ref
(
iter
->
second
);
}
return
optional_info_ref
();
});
}
void
wasm_interface
::
load
(
digest_type
codeid
,
const
char
*
code
,
size_t
codesize
)
{
std
::
unique_lock
<
std
::
mutex
>
lock
(
global_load_mutex
);
/**
* Opportunistically fetch an available instance of the code;
* @param code_id - the id of the code to fetch
* @return - reference to the entry when one is available
*/
optional_entry_ref
try_fetch_entry
(
const
digest_type
&
code_id
)
{
return
with_lock
([
&
,
this
](){
auto
iter
=
_cache
.
find
(
code_id
);
if
(
iter
!=
_cache
.
end
()
&&
iter
->
second
.
available_instances
>
0
)
{
auto
&
ptr
=
iter
->
second
.
instances
.
at
(
iter
->
second
.
available_instances
--
);
return
optional_entry_ref
(
*
ptr
);
}
FC_ASSERT
(
codeid
!=
digest_type
()
);
auto
&
state
=
my
->
code_cache
[
codeid
];
if
(
state
.
code_version
==
codeid
)
{
my
->
current_state
=
&
state
;
return
;
/// already cached
return
optional_entry_ref
();
});
}
state
.
module
=
new
IR
::
Module
();
/**
* Fetch a copy of the code, this is guaranteed to return an entry IF the code is compilable.
* In order to do that in safe way this code may cause the calling thread to sleep while a new
* version of the code is compiled and inserted into the cache
*
* @param code_id - the id of the code to fetch
* @param wasm_binary - the binary for the wasm
* @param wasm_binary_size - the size of the binary
* @return reference to a usable cache entry
*/
wasm_cache
::
entry
&
fetch_entry
(
const
digest_type
&
code_id
,
const
char
*
wasm_binary
,
size_t
wasm_binary_size
)
{
std
::
condition_variable
condition
;
optional_entry_ref
result
;
std
::
exception_ptr
error
;
// compilation is not thread safe, so we dispatch it to a io_service running on a single thread to
// queue up and synchronize compilations
_ios
.
post
([
&
,
this
](){
// check to see if someone returned what we need before making a new one
auto
pending_result
=
try_fetch_entry
(
code_id
);
std
::
exception_ptr
pending_error
;
if
(
!
pending_result
)
{
// time to compile a brand new (maybe first) copy of this code
Module
*
module
=
new
Module
();
ModuleInstance
*
instance
=
nullptr
;
size_t
mem_end
;
vector
<
char
>
mem_image
;
try
{
Serialization
::
MemoryInputStream
stream
((
const
U8
*
)
code
,
code
size
);
WASM
::
serializeWithInjection
(
stream
,
*
state
.
module
);
Serialization
::
MemoryInputStream
stream
((
const
U8
*
)
wasm_binary
,
wasm_binary_
size
);
WASM
::
serializeWithInjection
(
stream
,
*
module
);
root_resolver
resolver
;
LinkResult
link_result
=
linkModule
(
*
state
.
module
,
resolver
);
state
.
instance
=
instantiateModule
(
*
state
.
module
,
move
(
link_result
.
resolvedImports
)
);
FC_ASSERT
(
state
.
instance
);
LinkResult
link_result
=
linkModule
(
*
module
,
resolver
);
instance
=
instantiateModule
(
*
module
,
std
::
move
(
link_result
.
resolvedImports
)
);
FC_ASSERT
(
instance
!=
nullptr
);
auto
current_memory
=
Runtime
::
getDefaultMemory
(
state
.
instance
);
auto
current_memory
=
Runtime
::
getDefaultMemory
(
instance
);
char
*
memstart
=
&
memoryRef
<
char
>
(
current_memory
,
0
);
const
auto
allocated_memory
=
Runtime
::
getDefaultMemorySize
(
state
.
instance
);
for
(
uint64_t
i
=
0
;
i
<
allocated_memory
;
++
i
)
{
if
(
memstart
[
i
]
)
{
state
.
mem_end
=
i
+
1
;
char
*
mem_ptr
=
&
memoryRef
<
char
>
(
current_memory
,
0
);
const
auto
allocated_memory
=
Runtime
::
getDefaultMemorySize
(
instance
);
for
(
uint64_t
i
=
0
;
i
<
allocated_memory
;
++
i
)
{
if
(
mem_ptr
[
i
])
{
mem_end
=
i
+
1
;
}
}
state
.
init_memory
.
resize
(
state
.
mem_end
);
memcpy
(
state
.
init_memory
.
data
(),
memstart
,
state
.
mem_end
);
state
.
code_version
=
codeid
;
mem_image
.
resize
(
mem_end
);
memcpy
(
mem_image
.
data
(),
mem_ptr
,
mem_end
);
}
catch
(...)
{
pending_error
=
std
::
current_exception
();
}
catch
(
Serialization
::
FatalSerializationException
exception
)
{
std
::
cerr
<<
"Error deserializing WebAssembly binary file:"
<<
std
::
endl
;
std
::
cerr
<<
exception
.
message
<<
std
::
endl
;
throw
;
if
(
pending_error
==
nullptr
)
{
// grab the lock and put this in the cache as unavailble
with_lock
([
&
,
this
]()
{
// find or create a new entry
auto
iter
=
_cache
.
emplace
(
code_id
,
code_info
{
.
mem_end
=
mem_end
,
.
mem_image
=
std
::
move
(
mem_image
)
}).
first
;
iter
->
second
.
instances
.
emplace_back
(
std
::
make_unique
<
wasm_cache
::
entry
>
(
instance
,
module
));
pending_result
=
optional_entry_ref
(
*
iter
->
second
.
instances
.
back
().
get
());
});
}
catch
(
IR
::
ValidationException
exception
)
{
std
::
cerr
<<
"Error validating WebAssembly binary file:"
<<
std
::
endl
;
std
::
cerr
<<
exception
.
message
<<
std
::
endl
;
throw
;
}
catch
(
std
::
bad_alloc
)
// publish result under lock
with_lock
([
&
](){
if
(
pending_error
!=
nullptr
)
{
error
=
pending_error
;
}
else
{
result
=
pending_result
;
}
});
condition
.
notify_all
();
});
// wait for the other thread to compile a copy for us
{
std
::
cerr
<<
"Memory allocation failed: input is likely malformed"
<<
std
::
endl
;
throw
;
std
::
unique_lock
<
std
::
mutex
>
lock
(
_cache_lock
);
condition
.
wait
(
lock
,
[
&
]{
return
error
!=
nullptr
||
result
.
valid
();
});
}
my
->
current_state
=
&
state
;
}
/// wasm_interface::load
void
wasm_interface
::
init
(
apply_context
&
context
)
{
try
{
try
{
FunctionInstance
*
init
=
asFunctionNullable
(
getInstanceExport
(
my
->
current_state
->
instance
,
"init"
));
if
(
!
init
)
return
;
/// if not found then it is a no-op
if
(
error
!=
nullptr
)
{
std
::
rethrow_exception
(
error
);
}
else
{
return
(
*
result
).
get
();
}
}
FC_RETHROW_EXCEPTIONS
(
error
,
"error compiling WASM for code with hash: ${code_id}"
,
(
"code_id"
,
code_id
));
}
/**
* return an entry to the cache. The entry is presumed to come back in a "dirty" state and must be
* sanitized before returning to the "available" state. This sanitization is done asynchronously so
* as not to delay the current executing thread.
*
* @param code_id - the code Id associated with the instance
* @param entry - the entry to return
*/
void
return_entry
(
const
digest_type
&
code_id
,
wasm_cache
::
entry
&
entry
)
{
_ios
.
post
([
&
,
this
](){
// sanitize by reseting the memory that may now be dirty
auto
&
info
=
(
*
fetch_info
(
code_id
)).
get
();
char
*
memstart
=
&
memoryRef
<
char
>
(
getDefaultMemory
(
entry
.
instance
),
0
);
memset
(
memstart
+
info
.
mem_end
,
0
,
((
1
<<
16
)
-
info
.
mem_end
)
);
memcpy
(
memstart
,
info
.
mem_image
.
data
(),
info
.
mem_end
);
// under a lock, put this entry back in the available instances side of the instances vector
with_lock
([
&
,
this
](){
// walk the vector and find this entry
auto
iter
=
info
.
instances
.
begin
();
while
(
iter
->
get
()
!=
&
entry
)
{
++
iter
;
}
//checktimeStart = fc::time_point::now(
);
FC_ASSERT
(
iter
!=
info
.
instances
.
end
(),
"Checking in a WASM enty that was not created properly!"
);
const
FunctionType
*
functype
=
getFunctionType
(
init
);
FC_ASSERT
(
functype
->
parameters
.
size
()
==
0
);
auto
first_unavailable
=
(
info
.
instances
.
begin
()
+
info
.
available_instances
);
if
(
iter
!=
first_unavailable
)
{
std
::
swap
(
iter
,
first_unavailable
);
}
info
.
available_instances
++
;
});
});
}
std
::
vector
<
Value
>
args
(
0
);
// mapping of digest to an entry for the code
map
<
digest_type
,
code_info
>
_cache
;
std
::
mutex
_cache_lock
;
Runtime
::
invokeFunction
(
init
,
args
);
// compilation and cleanup thread
std
::
thread
_utility_thread
;
io_service
_ios
;
optional
<
io_service
::
work
>
_work
;
};
}
catch
(
const
Runtime
::
Exception
&
e
)
{
edump
((
string
(
describeExceptionCause
(
e
.
cause
))));
edump
((
e
.
callStack
));
throw
;
wasm_cache
::
wasm_cache
()
:
_my
(
new
wasm_cache_impl
()
)
{
}
}
FC_CAPTURE_AND_RETHROW
()
}
/// wasm_interface::init
void
wasm_interface
::
apply
(
apply_context
&
context
)
{
try
{
wasm_cache
::~
wasm_cache
()
=
default
;
wasm_cache
::
entry
&
wasm_cache
::
checkout
(
const
digest_type
&
code_id
,
const
char
*
wasm_binary
,
size_t
wasm_binary_size
)
{
// see if there is an avaialble entry in the cache
auto
result
=
_my
->
try_fetch_entry
(
code_id
);
if
(
result
)
{
return
(
*
result
).
get
();
}
return
_my
->
fetch_entry
(
code_id
,
wasm_binary
,
wasm_binary_size
);
}
void
wasm_cache
::
checkin
(
const
digest_type
&
code_id
,
entry
&
code
)
{
_my
->
return_entry
(
code_id
,
code
);
}
/**
* RAII wrapper to make sure that the context is cleaned up on exception
*/
struct
scoped_context
{
template
<
typename
...
Args
>
scoped_context
(
optional
<
wasm_context
>
&
context
,
Args
&
...
args
)
:
context
(
context
)
{
context
=
wasm_context
{
args
...
};
}
~
scoped_context
()
{
context
.
reset
();
}
optional
<
wasm_context
>&
context
;
};
void
wasm_interface_impl
::
call
(
const
string
&
entry_point
,
const
vector
<
Value
>&
args
,
wasm_cache
::
entry
&
code
,
apply_context
&
context
)
try
{
FunctionInstance
*
call
=
asFunctionNullable
(
getInstanceExport
(
my
->
current_state
->
instance
,
"apply"
)
);
FunctionInstance
*
call
=
asFunctionNullable
(
getInstanceExport
(
code
.
instance
,
entry_point
)
);
if
(
!
call
)
{
return
;
}
//FC_ASSERT( apply, "no entry point found for ${call}", ("call", std::string(name)) );
FC_ASSERT
(
getFunctionType
(
call
)
->
parameters
.
size
()
==
2
);
// idump((current_validate_context->msg.code)(current_validate_context->msg.type)(current_validate_context->code));
vector
<
Value
>
args
=
{
Value
(
uint64_t
(
context
.
act
.
scope
)),
Value
(
uint64_t
(
context
.
act
.
name
))
};
auto
context_guard
=
scoped_context
(
current_context
,
code
,
context
);
Runtime
::
invokeFunction
(
call
,
args
);
}
catch
(
const
Runtime
::
Exception
&
e
)
{
FC_THROW_EXCEPTION
(
wasm_execution_error
,
"cause: ${cause}
\n
${callstack}"
,
(
"cause"
,
string
(
describeExceptionCause
(
e
.
cause
)))
(
"callstack"
,
e
.
callStack
));
}
FC_CAPTURE_AND_RETHROW
()
wasm_interface
::
wasm_interface
()
:
my
(
new
wasm_interface_impl
()
)
{
}
auto
&
state
=
*
my
->
current_state
;
char
*
memstart
=
&
memoryRef
<
char
>
(
getDefaultMemory
(
my
->
current_state
->
instance
),
0
);
memset
(
memstart
+
state
.
mem_end
,
0
,
((
1
<<
16
)
-
state
.
mem_end
)
);
memcpy
(
memstart
,
state
.
init_memory
.
data
(),
state
.
mem_end
);
wasm_interface
&
wasm_interface
::
get
()
{
static
bool
init_once
=
[](){
Runtime
::
init
();
return
true
;
}();
boost
::
ignore_unused
(
init_once
);
//checktimeStart = fc::time_point::now();
my
->
current_state
->
context
=
&
context
;
Runtime
::
invokeFunction
(
call
,
args
);
my
->
current_state
->
context
=
nullptr
;
thread_local
wasm_interface
*
single
=
nullptr
;
if
(
!
single
)
{
single
=
new
wasm_interface
();
}
return
*
single
;
}
}
catch
(
const
Runtime
::
Exception
&
e
)
{
edump
((
std
::
string
(
describeExceptionCause
(
e
.
cause
))));
edump
((
e
.
callStack
));
throw
;
void
wasm_interface
::
init
(
wasm_cache
::
entry
&
code
,
apply_context
&
context
)
{
my
->
call
(
"init"
,
{},
code
,
context
);
}
}
FC_CAPTURE_AND_RETHROW
()
}
/// wasm_interface::apply
Runtime
::
MemoryInstance
*
wasm_interface
::
memory
()
const
{
return
Runtime
::
getDefaultMemory
(
my
->
current_state
->
instance
);
void
wasm_interface
::
apply
(
wasm_cache
::
entry
&
code
,
apply_context
&
context
)
{
vector
<
Value
>
args
=
{
Value
(
uint64_t
(
context
.
act
.
scope
)),
Value
(
uint64_t
(
context
.
act
.
name
))
};
my
->
call
(
"apply"
,
args
,
code
,
context
);
}
uint32_t
wasm_interface
::
memory_size
()
const
{
return
Runtime
::
getDefaultMemorySize
(
my
->
current_state
->
instance
);
void
wasm_interface
::
error
(
wasm_cache
::
entry
&
code
,
apply_context
&
context
)
{
vector
<
Value
>
args
=
{
/* */
};
my
->
call
(
"error"
,
args
,
code
,
context
);
}
#if 0
...
...
@@ -327,7 +535,7 @@ class intrinsics {
public:
intrinsics
(
wasm_interface
&
wasm
)
:
wasm
(
wasm
)
,
context
(
*
intrinsics_accessor
::
get_module_state
(
wasm
).
context
)
,
context
(
intrinsics_accessor
::
get_context
(
wasm
).
context
)
{}
int
read_action
(
array_ptr
<
char
>
memory
,
size_t
size
)
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录