-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve exception handling in memory.grow #737
Conversation
Codecov Report
@@ Coverage Diff @@
## master #737 +/- ##
==========================================
- Coverage 99.35% 99.26% -0.10%
==========================================
Files 73 73
Lines 11388 11398 +10
==========================================
- Hits 11315 11314 -1
- Misses 73 84 +11
Flags with carried forward coverage won't be shown. Click here to find out more.
|
lib/fizzy/execute.cpp
Outdated
{ | ||
ret = static_cast<uint32_t>(-1); | ||
} | ||
catch (const std::length_error&) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't look possible to cover it, for me std::string::max_size()
returns 2^62-1 bytes (2^46 pages), we can't allocate more than that with wasm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is worth having considering max_size()
is not very useful.
It may be just better to add a unit test that checks if wasm memory max size is not smaller than std::string::max_size()
.
Alternatively, you can catch std::exception
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On 32-bit build the value is 2^31-1.
d5e66c6
to
3951f10
Compare
lib/fizzy/execute.cpp
Outdated
@@ -840,7 +840,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, | |||
throw std::bad_alloc(); | |||
memory->resize(new_pages * PageSize); | |||
} | |||
catch (std::bad_alloc const&) | |||
catch (const std::exception&) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment that std::bad_alloc
and std::legth_error
are expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ... catches everything so it guarantees noexcept outside. The std::exception does not, so there will be another compiler generated check ending in std::terminate or something.
So my final call is to use ... and also add an assert that the exception is one of the two expected types.
Although, checking the exception type may be tricky. Probably a helper is needed as presented in https://en.cppreference.com/w/cpp/error/rethrow_exception
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well then in the helper catch std::length_error
will still be not covered, it's simpler then to have here catch (bad_alloc)
+ catch (length_error)
+ catch (...) { assert(false); }
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well then in the helper
catch std::length_error
will still be not covered, it's simpler then to have herecatch (bad_alloc)
+catch (length_error)
+catch (...) { assert(false); }
.
But yeah execute
will be smaller if extra catches go into helper, and helper will should be omitted in release build...
3951f10
to
71e4ac2
Compare
71e4ac2
to
ce2bca6
Compare
lib/fizzy/execute.cpp
Outdated
@@ -499,6 +499,25 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan | |||
return true; | |||
} | |||
|
|||
[[maybe_unused]] bool is_resize_exception(std::exception_ptr exception) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Perhaps you want to add a comment here explaining our use case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment.
lib/fizzy/execute.cpp
Outdated
@@ -840,8 +859,9 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, | |||
throw std::bad_alloc(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this use std::bad_alloc
or std::length_error
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bad_alloc
is better but not perfect fit out of these two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, here I'd see another refactoring: do not use throw
.
uint32_t ret = (new_pages <= instance.memory_pages_limit) ? static_cast<uint32_t>(cur_pages) : static_cast<uint32_t>(-1);
try {
memory->resize(new_pages * PageSize);
} catch (...) { ... }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't go to resize
though, if it's over the limit.
I can make a version with if
, but it looks somewhat uglier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
bad_alloc
is better but not perfect fit out of these two.
Why it it better and what's the "fit" here? I'd say requirement for memory.grow
to fail in case requested size is out of bounds is similar to requirement for basic_string
to throw length_error
in case count > max_size()
.
bad_alloc
represents failure of some lower level allocation mechanism.
But I think in practice it doesn't matter and I'm fine with either.
ce2bca6
to
7d5cd5a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not very beautiful, but I think this is fine.
It is rather visible that some Memory
abstraction type would be nice. This can be done together with "guarded pages" later.
There is also an issue on 32-bit architectures where string/bytes can only allocate up to 2GB of memory.
lib/fizzy/execute.cpp
Outdated
} | ||
catch (std::bad_alloc const&) | ||
{ | ||
uint32_t ret; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, wondering if the following would be more readable:
uint32_t ret = static_cast<uint32_t>(-1);
if (new_pages <= instance.memory_pages_limit)
{
try
{
memory->resize(new_pages * PageSize);
ret = static_cast<uint32_t>(cur_pages);
}
catch (...)
{
assert(is_resize_exception(std::current_exception()));
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks fine, I don't mind.
The best is to wrap this into uint32_t Memory::grow(something);
. Then you eliminate ret
housekeeping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can move it into a simple helper for now.
6af194f
to
9d92654
Compare
lib/fizzy/execute.cpp
Outdated
ret = static_cast<uint32_t>(-1); | ||
} | ||
stack.push(ret); | ||
stack.push(grow_memory(*memory, delta, instance.memory_pages_limit)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point could just use the stack pointer and no pop/push is needed 🙊
9d92654
to
c43e129
Compare
} | ||
} | ||
|
||
inline uint32_t grow_memory( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are going with this helper, please document it. What is important to document that it returns page count prior to expansion.
c43e129
to
3864569
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder how this affects benchmarks as it could mean quite a different binary layout 🙊
No description provided.