<div dir="ltr">I think that I didn't explain myself correctly.<div><br></div><div>Let me try again.</div><div><br></div><div>I'm writing a C library, and I want it to be useful not only in Linux userland, but also in other contexts, such as embedded devices, and inside the Linux kernel.</div><div><br></div><div>This library sometimes allocates memory.</div><div><br></div><div>If I'll just allocate memory with malloc, my library wouldn't even compile with embedded devices. Hence, I'll receive an allocator function from the user, and use it to allocate memory.</div><div><br></div><div>A concrete example, a "regular" read_line function</div><div><br></div><div>char *read_line(struct reader *r) { char *rv = malloc(len); read_to(rv); return rv; }</div><div><br></div><div>A more flexible read_line function:</div><div><br></div><div>char *read_line(struct reader *r, struct mem_pool *pool) { char *rv = pool->alloc(pool, len); read_to(rv); return rv; }</div><div><br></div><div>Now I'm fine, because I can define pool->alloc to be kmalloc in the kernel context, or use preallocated pools in embedded device.</div><div><br></div><div>What should I do if memory allocation fails? I can return an error for the user, but it makes the API more complicated. Since many functions would have to return error just in case of memory allocation failure.</div><div><br></div><div>Our read_line example would now look like</div><div><br></div><div>struct error read_line(struct reader *r, char **line);</div><div><br></div><div>And users would have to check error at each invocation.</div><div><br></div><div>I think I can avoid that. What do I intend to do?</div><div><br></div><div>My library would assume each memory allocation is successful, and the client that provides the memory allocator would be responsible to failure handling.</div><div><br></div><div>For example, in a "regular" linux userspace program, the mem_pool would be something like</div><div><br></div><div>void *linux_userspace_mem_pool(struct mem_pool *pool, int size) {</div><div>   void *rv = malloc(size);</div><div>   if (rv == NULL) {</div><div>       syslog("ENOMEM");</div><div>       exit(0);</div><div>   }</div><div>}</div><div><br></div><div>An embedded client could throw an exception, or longjmp to the main loop, or reset the system.</div><div><br></div><div>Now my question is, is that a reasonable behavior that would suite embedded devices. I do not have enough experience to know that. Indeed, since I'm writing a library that I hope would serve as broad audience as possible, it is hard to know the requirements in advance.</div><div><br></div><div>Hence, I think 1-4 are already addressed, I always gives the user control what would happen when he's out of memory.</div><div><br></div><div>Regarding 5-6. What I'm saying is, seeing malloc returning NULL in production is very rare. I personally never seen that. I think that the OOM killer would wreck havoc to the system before it would happen, hence, crashing when malloc returns NULL is a reasonable behavior.</div><div><br></div><div>Regarding 7, this does not complicate the allocation API, it complicates my API, since I'll have functions that cannot fail, generally speaking, but allocates memory. Those would have to return error, and the user would have to check the error.</div><div><br></div><div>Thanks,</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sat, May 16, 2015 at 11:18 PM, Oleg Goldshmidt <span dir="ltr"><<a href="mailto:pub@goldshmidt.org" target="_blank">pub@goldshmidt.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">Elazar Leibovich <<a href="mailto:elazarl@gmail.com">elazarl@gmail.com</a>> writes:<br>
<br>
> My question is, should I support the case of malloc failure. On one<br>
> hand, it complicates the API significantly, but on the other hand it<br>
> might be useful for some use cases.<br>
<br>
</span>This sounds like, can you guys tell me what my requirements are? ;-)<br>
<br>
If I understand correctly, you want to provide an alternative (to the<br>
standard malloc() and friends) mechanism for memory allocation,<br>
targeting primarily embedded systems. If I am not completely wrong,<br>
consider the following:<br>
<br>
1. Is "mechanism" the operative word? If so, then you should leave<br>
   *policies* - including exception handling - to the client. If you<br>
   intend to restrict your library to a single OOM excepton policy you<br>
   should document the restricton. E.g., if your policy is going to be<br>
   "segfault" or "commit a clean(ish) seppuku", you should tell<br>
   potential users, using big bold red letters, "if this doesn't suit you<br>
   don't use the library."  How much this will affect your library's<br>
   usefulness/popularity I don't care to predict.<br>
<br>
2. Naively, I cannot imagine *not* letting clients of a<br>
   production-quality library decide what to do, if only to write<br>
   something sensible to a log using the client's preferred format and<br>
   destination. Some 20 year ago I saw popular (numerical) libraries<br>
   whose authors (probably members of the academia) considered abort a<br>
   legitimate way of handling failures. A scientist running a numerical<br>
   application with the ultimate purpose of writing a paper certainly is<br>
   justified in thinking that way. We, however, disqualified those<br>
   libraries for any production use for that reason alone, regardless of<br>
   their other qualities. IIRC we liked one of them enough to find *all*<br>
   the places where it aborted and modify the code (FOSS rules, huh?).<br>
<br>
3. There are enough examples of custom allocators. I am sure you can<br>
   find an awful lot of code, say, overriding new/delete in C++. Even<br>
   the standard libraries provide for overriding allocators. Find a few<br>
   reputable example, see how exceptions are handled, follow the<br>
   pattern? I suspect in most cases it is left to the library clients<br>
   (arguably easier with longjumping exceptions than with C-style error<br>
   propagation, but the point is, library code does not decide,<br>
   usually).<br>
<br>
4. What *are* your requirements? If a git client (an example you cited)<br>
   tries to malloc, gets NULL, tries to recover, and then gives up and<br>
   dies writing something to stderr, that's one thing. An embedded<br>
   device just crashing without telling anyone what's wrong? Maybe a<br>
   different kettle of fish altogether. Do you target devices with<br>
   limited or somewhat limited - resources? May make a difference.<br>
<br>
5. You mentioned swapping. That does not mean you are out of memory<br>
   (malloc does not fail whan you swap pages). But I am sure you know<br>
   that.<br>
<br>
6. Kernel's OOM killer mechanism is also not directly related to<br>
   malloc() failing. It means that *some* process, not necessarily (or<br>
   even likely) the process that is requesting memory at the moment,<br>
   will be killed, according to some policy. No one can decide in<br>
   advance whether killing *something* is a good decision in an<br>
   unspecified embedded system.<br>
<br>
7. Why do you say handling failures will complicate the API a lot? It is<br>
   not clear from what you wrote. After all, malloc() is not more<br>
   complex because it can return NULL, is it? So can your alloc() member<br>
   - what's the problem?<br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
Oleg Goldshmidt | <a href="mailto:pub@goldshmidt.org">pub@goldshmidt.org</a><br>
</font></span></blockquote></div><br></div>