Concurrency with Erlang

I n the last issue, guest columnist Rachid Guerraoui of EPFL discussed the difficulties of developing multithreaded systems for today’s (and tomorrow’s) multicore CPUs, and he presented the notion of “free objects,” an extension of object-oriented (OO) programming, as a possible solution. Virtually everyone who has written a multithreaded library or application has had to deal with the mysterious program behavior and hard-to-find bugs that come with the territory. Despite a great deal of research and practice applied over the years in the hope of making things easier for developers, writing a concurrent application remains a difficult task for all but the elite developers Guerraoui identified as “concurrency aristocrats. ” It’s not an exaggeration to say that writing correct multithreaded programs is beyond many programmers’ technical abilities. One of the primary reasons concurrency is so hard is that popular imperative programming languages such as Java and C++ essentially require state to be shared among threads. In such languages, program pathways provide access for reading and writing thread-independent state variables. With multiple threads running on multiple CPUs, more than one pathway can be active simultaneously, and without the appropriate safeguards in place, the threads can read partial values and overwrite each other’s data, resulting in bogus values and application instability. Programmers writing multithreaded applications in languages like Java and C++ spend much of their time determining what state is shared among threads and how best to protect its integrity within the running application. Finding all the shared state isn’t always easy. Assuming the programmer can even find it, he or she must then possess the skills, experience, and patience necessary to determine the best way to serialize access to it. Adding to the difficulty is the locking granularity used to protect shared state. If locking is too coarse-grained, the application tends toward single-threading, because only one thread at a time can obtain the lock that predictably surrounds a large portion of the code. Such applications make poor use of multicore CPUs and tend to be slow. If locking is too fine-grained, on the other hand, the chances for deadlock increase greatly as different threads are increasingly likely to obtain locks in different orders. Getting it right takes time: fine tuning what state needs protection, and at what granularity, sometimes requires months of development time. Idioms, patterns, and frameworks can help out partially, but they introduce restrictions and trade-offs of their own. Ultimately, all it takes is one developer overlooking one small piece of shared state to cause a large system to crash in production — usually at the worst possible time for the most important customer. One way to avoid the problems with shared state is to simply avoid it, but that’s impractical in a language like C++ or Java. Doing so requires a combination of libraries or frameworks such as those based on actor models and message passing, as Guerraoui described. It also requires significant programmer discipline because the programming languages themselves can’t do anything to help developers completely circumvent shared state. Rather than using the programming language as intended, the developer is forced to write in what’s at best a oneoff dialect or worse, a whole new one-off language, using the special idioms and frameworks designed to help avoid sharing state among threads. A better way to avoid shared state is to switch to a programming language specifically designed to do exactly that. Erlang is one such language.