5 ms·
The `realloc` problem isn't the only one - it also breaks many formerly-well-defined programs that use `malloc_usable_size`. Instead of fixing this, the develo
by o11c 2y ago
The `realloc` problem isn't the only one - it also breaks many formerly-well-defined programs that use `malloc_usable_size`.
Instead of fixing this, the developers behind the "dynamic object size" push have been changing the documentation to declare any use of `malloc_usable_size` buggy even though it used to be explicitly documented as safe.
I suspect that GCC's optimization passes will break even C-standard-compliant use of `realloc`, similar to how ASAN can break due to dynamic equality checks between pointers of different provenance.
Life would be much simpler for many of us if the standards committee bothered to standardize a function that says "give me a buffer of semi-arbitrary size and tell me how big it is; I promise to resize it later", which is very widely intended. An explicit "realloc in place only, else fail" would also make many more useful programs feasible to write.
- dwattttt 2y ago> give me a buffer of semi-arbitrary size and tell me how big it is; I promise to resize it later I'm not sure how you could use this usefully. If you don't care what size you get, why would you allocate in the first place? And if you do have a minimum size you need right now, and a need for it to be bigger later, isn't that what a malloc/realloc dance is for?
- freeone3000 2y agoExample: reading a file line-by-line. You don’t, can’t, know how big a line is. Your best option is to allocate some random chunk of memory, like 4 kilobytes, read 4 kilobytes from the file into memory, and see if you happened across a newline in there. If you did, shuffle everything around a little / realloc / ringbuffer shenanigans; do your favourite. If you didn’t, make the buffer bigger (by 1.5x? 2x? Log2x?), and try again. This dance is super common with variable length protocols over unframed streams - ie, most things over tcp. So this is an exceptionally common pattern in IO operations. Other common times this pattern happens: finding all items in a list that satisfy a predicate; tracking items a user has added to an interface; cycle detection in graphs; …
- dwattttt 2y agoI don't think this or the sibling would be improved with a function that gave you an arbitrarily sized allocation where you also need to query the size of that allocation though? In all the cases you need to know what the size of the buffer is, even though you don't care whether it's 1kb, 4kb, or 8kb (although I imagine you'd care if you got 16b or 16gb)
- nickelpro 2y agoIt's not about querying the size, it's about what the allocator has available without having to ask the operating system for new pages. It's got a contiguous block of 6371 bytes? Cool I'll take that, the specific size didn't matter that much.
- nine_k 2y agoE.g. "Give me something in the ballpark of 4 KiB; more is OK, but tell me how much". It would allow the allocator to return a chunk it has handy, instead of cutting it to size and fragmenting things, only to be asked to make the chunk larger a few microseconds later. It will save both on housekeeping and fragmentation handling if the caller knows that the chunk will likely need to grow.
- celrod 2y agoC++23 added `allocate_at_least`: https://en.cppreference.com/w/cpp/memory/allocator_traits/allocate_at_least https://en.cppreference.com/w/cpp/memory/allocator_traits/al... I'm not sure if any standard libraries have an implementation that takes advantage of the "at least" yet.
- o11c 2y agoCases where the exact buffer size doesn't matter are ubiquitous, for example: * read a file(-like) in streaming mode. Whenever the buffer is empty, fill it. The actual allocated size does not matter at all for most kinds of file. * push objects onto a vector when the capacity is used up, reallocate at a larger size and keep pushing. The actual allocated capacity doesn't matter at any point. * implement a bloom filter for approximate set membership. If the allocator happens to give you a little more than your mathematical estimation for the chances you want, you might as well use it. In fact, I dare say: every allocation size that is neither `0` or `sizeof(T)` doesn't fundamentally care about the size (a given implementation may care, but if the standard bothered to implement useful new functionality we would change the implementations). This is unlike, say, my desire for skewed-alignment allocators, which is but not particularly useful for most programs.
- nickelpro 2y agoI hadn't thought about the `sizeof(T)` point before, and I can think of a handful of exceptions, but it's a great and expressive rule of thumb.
- dataflow 2y ago> In fact, I dare say: every allocation size that is neither `0` or `sizeof(T)` doesn't fundamentally care about the size Not true? When you copy an array (common case: string) that is passed to you, you want the copy to the the same size. No more (that wastes memory) and no less (then you don't have a copy), just as with sizeof(T).
- kzrdude 2y agoIt's a performance optimization for growing datastructures. If you can use all the space that was actually allocated, you can (on average) call realloc less often when the container is growing dynamically.
- deleted 2y ago[deleted]
- deleted 2y ago[deleted]
- usefulcat 2y ago> it also breaks many formerly-well-defined programs that use `malloc_usable_size` Does it? According to the documentation for malloc_reusable_size, it can be used to find out the actual size of an allocation, but you still have to call realloc before writing to bytes beyond the size passed to malloc. "This function is intended to only be used for diagnostics and statistics; writing to the excess memory without first calling realloc(3) to resize the allocation is not supported."
- o11c 2y agoYes, that's what the documentation has been retroactively changed to say. It used to say that the memory was usable immediately. Also, `realloc` is not guaranteed to actually operate in-place.
- uecker 2y agoLife would be much simpler for many us if people would stop complaining on internet forms and starting contributing to open-source or standardization efforts.
- o11c 2y agoIs "don't go out and actively break programs you used to promise would work" such a great ask?
- uecker 2y agoAbout what are you talking exactly? What was actively broken?
- o11c 2y ago`__builtin_dynamic_object_size` has been buggy since it was first implemented, breaking programs that relied on the previously-documented behavior of `malloc_usable_size`. Instead of fixing the bug, the developers decided to remove the documentation and replace it with something else, breaking backwards compatibility. Now, it's not that we can't ever break backwards compatibility - but it needs to be done with great deliberation and a transition period, and an alternative needs to be provided. I gave an example of an alternative.
- uecker 2y agoThis seems not entirely accurate. "malloc_usable_size" was only recommended for use for statistics before the change in man page: "Although the excess bytes can be overwritten by the application without ill effects, this is not good programming practice:" and "The main use of this function is for debugging and introspection". The new version makes it clearer. You can find the change here: https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/commit/?id=015464751006a964ff401f1eb5945ca28c4448a7 https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/... Also no code is "actively broken" by a documentation change. If it happened to work before it still works today. Also I do not see how __builtin_dynamic_object_size is broken. It works as intended and can be used with conforming code just fine. It is simply not compatible with some questionable use of "malloc_usable_size".