Live data from Hacker News

In C, how do you know if the dynamic allocation succeeded?

lemire.me

121–130 of 198 posts

Re: In C, how do you know if the dynamic allocation succeeded?

#121
post #42

Earlier quoted context omitted.

But what happens when the kernel needs to copy one and has run out of memory? You still get a random process killed. (I note that Windows has a different approach, with "reserve" vs "commit", but nobody regards that as a preferential reason for using Windows as a server OS)

> But what happens when the kernel needs to copy one and has run out of memory? Don't allow it. Fork the process with read only pages except for the ranges passed to fork(). Count read-write pages as used memory by the child process. If the forked process wants to write to a page that's read-only it'll have to do a system call to turn it read-write. That call can then fail if there's not enough free memory to copy th…

> Don't allow it. Fork the process with read only pages except for the ranges passed to fork().

This doesn't work because of the horrible fork/exec design. If I am a huge process and I want to run `ls`, I will first have to clone myself using fork(), which may trigger an OOM, even if the first action of my clone would have been exec(), ignoring all of that memory.

I am always surprised that no one has added a sane 'spawn process' primitive to replace fork/exec. Especially since fork() without exec() only really works in single-threaded processes.

Re: In C, how do you know if the dynamic allocation succeeded?

#122

That’s one of the many reasons I think Windows NT kernel is generally better than Linux. Windows doesn’t do that. When you don’t have enough memory and not enough page file space either, these allocation functions usually do fail returning nullptr.

If you really want that you can just tell Linux not to overcommit. I can foresee your next question though, "Wait, why did I run out of memory? Stupid Linux, I have plenty of RAM".

Well, it would be fair to call overcommit-less Linux stupid. There are a number of aspects of Linux that are only reasonable if you assume that memory overcommit is a thing, and would be radically stupid to include in an OS that does not overcommit by default. For example, fork(), which only makes sense with memory overcommit and shared copy-on-write pages. Process is using 50.1% of the physical memory? Guess it can’t spawn processes, since it has to fork() before it can exec() and there’s not enough memory to copy the process!

I’m sure there are other horrendously degraded modes that Linux can theoretically operate in (read-only file system?, unreliable system clock?), but disabling overcommit, while possible, turns Linux into an exceedingly crummy OS.

Re: In C, how do you know if the dynamic allocation succeeded?

#123

Earlier quoted context omitted.

I mean, posix_spawn exists. It's a messy function, but its job is messy for exactly the reasons you describe. (FWIW, there are very few things you can legally perform between fork and exec.)

Are there any things that are illegal between fork and exec? It is perfectly legal to exec whenever you want, and it it perfectly legal to fork whenever you want. I'm not aware of any requirements around the fork/exec sequence.

For a single-threaded process, sure. But in a multi threaded context, almost any operation done in the child after fork() can royally mess up the system. There are many versions of libcs where malloc() after fork() is likely to deadlock between the parent and child processes, as they share the internal malloc() locks.

Re: In C, how do you know if the dynamic allocation succeeded?

#124

That’s one of the many reasons I think Windows NT kernel is generally better than Linux. Windows doesn’t do that. When you don’t have enough memory and not enough page file space either, these allocation functions usually do fail returning nullptr.

But this feature is also why malloc on Windows can take a long time to return - seconds or more if you malloc too much. I recently bumped into this problem on the GPU. Since GPU memory in the Windows Display Driver Model requires it to be backed by the host machine’s virtual memory, a malloc on the GPU when running low on space can end up searching & even defragging virtual memory to satisfy the request. Crazy! Just asking for RAM can cause you to land in heavy disk swap. I’ve seen cases of cudaMalloc taking minutes because of how Windows handles allocation, and the same is true of CPU mallocs.

Re: In C, how do you know if the dynamic allocation succeeded?

#125

Earlier quoted context omitted.

> But what happens when the kernel needs to copy one and has run out of memory? Don't allow it. Fork the process with read only pages except for the ranges passed to fork(). Count read-write pages as used memory by the child process. If the forked process wants to write to a page that's read-only it'll have to do a system call to turn it read-write. That call can then fail if there's not enough free memory to copy th…

> Don't allow it. Fork the process with read only pages except for the ranges passed to fork(). This doesn't work because of the horrible fork/exec design. If I am a huge process and I want to run `ls`, I will first have to clone myself using fork(), which may trigger an OOM, even if the first action of my clone would have been exec(), ignoring all of that memory. I am always surprised that no one has added a sane 's…

We do have posix_spawn &c., which are expressly designed to be implementable even in situations where fork doesn't make sense.

Re: In C, how do you know if the dynamic allocation succeeded?

#126
post #101

Earlier quoted context omitted.

I've found overcommit useful for some scientific computing applications, but you're right it's not the most intuitive default. You can disable memory overcommit on Linux for the same behavior as Windows, if you want. https://www.kernel.org/doc/Documentation/vm/overcommit-accou... Also, I found this post that suggests Windows technically does overcommit memory, but only for stacks(‽): https://superuser.com/questions/1…

Disabling overcommit on Linux doesn't tend to enable library and utility developers to have used memory responsibly. So it can be hard to run a system with common software.

I keep it disabled on one of my systems for exactly that reason: I want to make sure my own code works with overcommit disabled, as it'll always be on Windows or OpenBSD.

Re: In C, how do you know if the dynamic allocation succeeded?

#127
post #14

This is why some embedded systems don't do malloc (IIRC...been a while since I've read much about those).

Aside from the fact that overcommit usually doesn't apply to embedded systems, malloc() is fine; its free() that is the problem.

Re: In C, how do you know if the dynamic allocation succeeded?

#128

Earlier quoted context omitted.

Indeed. It's totally dumb that the OS is allowed lie to you when you've asked for some resources. And because of this behaviour people no longer check for success from malloc(), etc, because of laziness. It's a bad situation.

Let's say you malloc some memory and the computer actually has everything available. Everything is great, up until some other process on your system does a fork bomb of an infinitely recursive program that allocates nothing on the heap. You've just got a whole lot of quickly growing stacks hoovering up your physical memory pages.

Stack vs Heap is a userspace level concept. As far as the kernel memory manager knows, it's all just allocated memory - some is allocated by the process- or thread-spawning routines, some is allocated by malloc(), but they're using the exact same pool.

If overcommit is disabled and someone has allocated most system memory, fork() and exec() and pthread_create() etc. will theoretically fail with ENOMEM.

A bigger problem on Linux at least is that the kernel will swap out all possible memory before returning an allocation error. Even if you have not allocated any swap space, it will swap out any memory mapped files.

And even if none of your programs have explicitly mmap()ed anything, the actual application code is mapped, so it will start swapping out all code pages to disk before it refuses an allocation. And now this means that your system has become entirely unusable and will have to be hard rebooted, because it is now swapping data to and from disk on every instruction execution after every context switch. At least your CPU will get to stay nice and cool for a while.

Re: In C, how do you know if the dynamic allocation succeeded?

#129

Earlier quoted context omitted.

wouldn't you just account the COW pages against the parent until they are copied? kicking the can down the road means there isn't any longer a reasonable correction (failing the allocation), but instead we get to drive around randomly trying to find something to kill. this is particularly annoying if you are running a service. there is no hope for it to recover - for example by flushing a cache. instead the OS looks…

> wouldn't you just account the COW pages against the parent until they are copied? That's what Linux does. But how do you return ENOMEM when the copy does happen and now the system is out of memory? Memory writes don't return error codes. The best you could do is send a signal, which is exactly what the OOM killer does.

[deleted]

Re: In C, how do you know if the dynamic allocation succeeded?

#130

Earlier quoted context omitted.

I suppose vfork might help with that (though I don't understand why they don't directly add fork_and_execve, it would seem much easier). Also, the problem with Linux is not having overcommit, but notv being able to choose when to overcommit and when not. Windows makes that easier, AFAIU.

They do. Man posix_spawn. It comes with its own dsl to be able to support a small subset of all operations that are often performed between fork and execv. Vfork is usually a better solution. Edit: and yes, I would love to be able to disable overcommit per process.

> They do. Man posix_spawn. It comes with its own dsl to be able to support a small subset of all operations that are often performed between fork and execv.

In other words, you write your C code inside a C string instead of in C.

Post reply on HN