-
Notifications
You must be signed in to change notification settings - Fork 257
Handle malloc failures #68
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
Conversation
This is largely an alternative to #57. If someone who needs static memory (@stawiski? @RallyTronics?) could have a look at my custom allocators suggestion and see if it would work for them, that would be awesome. |
Good work, and really nice job on the write-up! I like your approach to the memory checks - honestly this is how it should have been done from the start. My only concern is that it adds a lot of lines of code. One of the selling points of this library is that it's small. Would you consider replacing some/most of the checks with a macro? I'm thinking
could become something like
It might not work for all cases, but I think it could still make it a lot more concise. |
That's a good idea. It takes this patch from a +168/-7 to a +90/-7. https://github.com/codeplea/tinyexpr/pull/68/files#diff-9dd905150ef73f001f71a847631eaf1c3c8cf53b7cb0ec921a200620fdf9cd04R375-R383 This was the only place I couldn't use the CHECK_NULL macro. However, if we changed |
a2311af
to
c8d5b43
Compare
Actually, nevermind. I didn't realize |
This approach can be interesting for applications allocation large blocks of data, but it is highly debatable whether there is a need for this kind of resource checking on such small amount of data we talk about here. If malloc fails in this library, your system probably either already bogged down to a halt, and there is small chances to recover anyway. On my 16GB mem windows machine I can currently allocate 45 GB with malloc (either in one block or in 1GB blocks), and resrouce monitor tells me I already use 9GBs. I personal feel these things should be managed by the OS in some way, and not by each and every small library. |
That's true in the very limited case of desktop/server Linux systems with overcommit enabled. It's not true for embedded, not true for Linux without overcommit, not for some other operating systems, not even true for Linux with overcommit enabled but with ulimits in place. Plus, even if it was 100% true in all cases, part of the reasoning for this patch is to support other allocation schemes such as allocating from one static buffer. It doesn't matter that malloc won't fail if you replace the allocator with one which allocates from a static 8k buffer. This is what the second part of the PR description was about. What could be interesting though is an option for disabling checked malloc, if that's actually a need people have. One could add an option to replace the CHECK_NULL macro with something like an empty |
I looked over this PR again, and the changes and overhead is minimal as you also claim. Thumbs up from me. |
@codeplea Could you merge this PR? |
This PR adds handling for malloc failures. I don't quite know how one would add sane test cases to cover it, but I did test it manually by replaceing malloc with a malloc implementation which returns NULL 10% of the time, and all of the existing test cases run fine, with no memory errors or memory leaks. The only problem was one place in
smoke.c
which uses the return value ofte_interp
without checking for errors.The approach to error checking is the most basic one; just add a whole bunch of
if (ret == NULL) { ...; return NULL; }
everywhere. It's not the prettiest, but it works. The alternative would be tosetjmp
at the beginning ofte_compile
, thenlongjmp
out ofnew_expr
. It would probably be faster, butsetjmp
andlongjmp
is honestly really scary, with a lot of space for accidentally stepping into undefined behavior (for example, all variables which are modified between thesetjmp
and thelongjmp
must be volatile). Alongjmp
-based approach would probably be faster though.The reason behind doing this isn't just that I think
malloc
failures should be handled in general. I also think this is a possible approach for custom allocators such as allocators which allocate from a pool of static memory. Here's how I imagine a viable path to statically allocated tinyexpr:TE_MALLOC
andTE_FREE
macros which default tomalloc
andfree
but can be overwritten at compile time.-DTE_MALLOC=static_te_malloc -DTE_FREE=static_te_free
to my tinyexpr.c compile options.I personally find this to be a cleaner, simpler and more flexible solution than adding explicit support for using a static memory pool to tinyexpr itself.
I decided to not include those macros in this PR, because it's kind of orthogonal, and I think handling malloc failures has value regardless of whether or not you agree with my idea for supporting custom allocators.
Obviously, adding extra
if
s everywhere has a performance cost (though only at te_compile time, not te_eval time). Here are times I got from runninghyperfine ./bench.orig ./bench.new
(wherebench.new
is the benchmarking suite with this patch):Here's the output from the unmodified benchmark suite on my machine:
And the benchmark suite with this patch: