Thursday, October 8, 2009

On Programming With Processes, Part II

One of the biggest challenges in building computer systems is finding a way to make things simpler. Any propeller-head can make a piece of software more complicated. Unfortunately, our industry seems to have a way of gravitating toward the complex. Let's look at the current state of the web browsers -- pick any one -- which seem to insist upon reimplementing or otherwise abusing the operating system.

Exhibit 1: About 2003, tabbed browsing is heralded as the wave of the future, and every web browser re-writes itself from scratch to support tabs and issues gushing press releases. What is a tab? Well, it's a way to switch between multiple running programs, each with its own title and visual space. Which is to say... it's like having windows! Except it's worse than having windows, it's like the old awful Multiple Document Interface, which even Microsoft now admits confused the heck out of everyone.

The funny thing is, you can achieve exactly the same behavior by dragging your taskbar to the top of the screen, like this:

Exhibit 2: You cannot run the latest version of Netscape (a.k.a Mozilla, Firefox, SeaMonkey, IceWeasel, good grief...) if your home directory is on a distributed file system. Never mind that putting your home directory on a shared filesystem is the normal practice in 90% of the industrialized world, where the user of the machine works for an organization that keeps important documents on a central server.

Apparently, Firefox uses an embedded database to store your preferences, bookmarks, cache, etc, and it cannot tolerate multiple simultaneous access. So, if you try to run multiple instances at once, it has to be clever enough to find the running copy and tell it to open a new window. If it cannot find it because the other copy is running in another console or on another machine, you get this ridiculous message:



Exhibit 3: Google Chrome is supposed to be the re-invention of the web browser, except simpler and more robust. Instead of threads, it uses this new-fangled technology called "processes" instead of those old gnarly threads. So far, so good. Then Firefox decides to get on this bandwagon.

Unfortunately, Firefox is missing the point entirely. The plan is to break the UI that controls all the windows into one process, and the plugins, parsers, renderers, etc into separate processes. It should come as no surprise that this makes things even more complicated, because the various pieces have to communicate with each other. More subtly, it makes the failure semantics really strange: if a helper process dies, one window will fail, but if the UI process dies, a whole bunch of windows will fail. If you look at the set of running processes, you are going to see an unpredictable number of processes with names that have no relation to what you are actually doing.

Everyone seems to have missed a ridiculously simple solution to all of these problems: Run each browser window in a separate process. You don't have to separate out all of the complex plugins, renderers, and so forth, because if one crashes, it will only take down that window. Furthermore, to open a new browser page in any context, all you have to do is fork() and exec("browser http://") and the operating system takes care of the rest.

See also: On Parallel Programming with Processes

7 comments:

  1. This would also make securing browser windows simpler - rather than worrying about data leakage between tabs and via browser cache, your independent processes could have their own security context. Better, you could change that security context per instance, either dynamically or at the user's discretion.

    In 2003 running more instances of browsers which often had memory leaks or had a bloated memory footprint would have been less attractive to me. Now, I'd probably find it a nice option, since I already run multiple browsers, often in VMs to achieve some of this functionality.

    ReplyDelete
  2. Tabs came about because the window management systems for all of the major platforms at the time sucked at managing large numbers of windows. (They still largely do suck, but it's improving.) You don't get help grouping related tasks while simultaneously keeping unrelated tasks separate. The space for showing and selecting between windows tended to get more awkward to work with as more and more windows were present.

    While not necessarily relevant to the average user, many advanced users will have a dozen or more web pages open at once. For this sort of work flow tabs were a great advancement. And of course tech writers are more likely than average to push their browser this way, so they appreciated it and advocated for it.

    I'm a bit surprised to the claim that Firefox doesn't work if your home directory (and more importantly ~/.mozilla) is on a distributed file system. The Firefox I'm using right now is doing exactly that and seems happy as a claim. The embedded database (SQLite) is perfectly happy to work on such a filesystem, although if multiple different processes will be accessing it simultaneously it does assume that fcntl file locking isn't broken.

    Now, Firefox itself does try to avoid having multiple copies at once. This is an old, old limitation. Firefox blindly rewrites a number of state files, including your bookmarks. Multiple browsers running creates race conditions and likely data loss. The addition of a proper database in the form SQLite makes this less likely, but the risk is still there for a number of files. So while it sucks and ideally Firefox should behave better, it's an old problem. It may be more visible than it used to be because Firefox is better at detecting the problematic state instead of silently putting your data at risk.

    As for having a new process for each web browser, it's a great idea. But the user with dozens of windows open at once again hits a problem: processes cost resources. This is exactly why browsers have been single processes since the early days. Chrome does exactly what you propose and it's a memory pig. (There are debates about the exact numbers, but the debate are about how much worse Chrome is, not that it's the same.) Firefox still endures a reputation for leaking memory, so they're naturally gun shy about a technique guaranteed to use more memory.

    ReplyDelete
  3. Alan:

    Note that if your browser is leaking memory, putting each window in its own process is better than putting each window in its own thread. That way, each window you close frees up all the memory, leaked or otherwise, associated with the window.

    ReplyDelete
  4. I don't buy the argument that processes are inherently more expensive in this context.

    Yes, a process is more expensive than a thread because in addition to registers and a stack, you need a page table, a file table, and other structures in the kernel. But, the cost of the process is absolutely nothing compared to the MB or GB of data being transferred between disk, network, and memory in order to display the page.

    The question is, does splitting a program in to two processes end up duplicating state in both processes? If so, then using multiple processes will result in waste.

    So, let's think about two processes, each one running a web browser and visiting a different page. Is there any waste that results from using two processes? Here are the components of the program.

    Executable - Both programs run the same executable, but the OS is smart enough to load the binary once and share it between both processes.

    Libraries - Both programs load the same libraries at runtime, but use mmap(), so the OS is smart enough to share them between the two processes.

    Stack - Each program needs its own stack to run the code. But, you would need two stacks in order to run two threads, so there is no waste here.

    Data - Each program needs to load a distinct web page and its images, scripts, etc, into memory. Because these are different sites, there is no opportunity for sharing, so processes neither help nor hurt. If you had two browsers open visiting the same page, then there would be waste.

    So, I would argue that there is no *fundamental* reason why using multiple processes should use more memory than multiple threads.

    I won't dispute the results above that show Chrome uses more memory than Firefox, but there are many many differences between those pieces of software that could be responsible for the difference.

    ReplyDelete
  5. It is really hard to measure how much memory a set of processes uses these days. Are you measuring virtual or physical? How do you account for shared memory segments? What about copy-on-write segments that are partially shared? What about the disk cache the OS lovingly provides for the processes?

    I suspect that a lot of these memory benchmarks that purport to compare process-based browsers (like chrome) with non-process ones, suffer from measurement errors.

    ReplyDelete
  6. I agree with Doug on threads vs processes for resource utilization. The increased memory usage (in my experience generally unimportant for web browsing -- i usually have upwards of 20 tabs, with music and video on my home machine) of the browser is likely insignificant when compared to the temporary data.

    At work I have run into the Mozilla error before, though it seems to be an infrequent problem and if I wait 20 seconds then it runs.

    Correct me if I am wrong -- what Firefox is planning to do seems to make the opposite of sense and worse than their current design?

    Also, does anyone know if Firefox is making such decisions to accommodate their plugin programmers to keep their numbers high and firefox a feature-loaded browser?

    ReplyDelete
  7. Doug, you mention firefox's proposed process separation as a flaw, but what you describe sounds exactly like what Google's Chrome browser does.

    Chrome has one process for the UI, one process for each plugin, and up to one process for each tab. (Tab behavior is complicated, because sometimes it allows multiple tabs for the same site to run in the same process, to allow inter-page communication using JavaScript session variables).

    The idea of this seperation is that the UI process would be very unlikely to crash. Crashes tend to come in when a page triggers a bug in the rendering engine, or when a plugin screws things up.

    Furthermore, I'm not aware of any windowing system that allows more than one process to present a window together seamlessly without having the processes scribble in each others memory space, which removes much of the advantage of being multiprocess rather than multithreaded. Thus there is no way to support things like tabbed browsing without having one thread for the GUI of all tabs.

    Your comment that browsers are reimplementing parts of operating systems is interesting, and something not unnoticed by the browser authors. Google Chrome has a task manager, for example.

    Further Google's ChromeOS has taken the browser and made it the entire UI of an operating system.

    ReplyDelete