You are previewing Erlang Programming.

Erlang Programming

Cover of Erlang Programming by Simon Thompson... Published by O'Reilly Media, Inc.
  1. Erlang Programming
    1. SPECIAL OFFER: Upgrade this ebook with O’Reilly
    2. Foreword
    3. Preface
      1. Francesco: Why Erlang?
      2. Simon: Why Erlang?
      3. Who Should Read This Book?
      4. How to Read This Book
      5. Conventions Used in This Book
      6. Using Code Examples
      7. Safari® Books Online
      8. How to Contact Us
      9. Acknowledgments
    4. 1. Introduction
      1. Why Should I Use Erlang?
      2. The History of Erlang
      3. Erlang’s Characteristics
      4. Erlang and Multicore
      5. Case Studies
      6. How Should I Use Erlang?
    5. 2. Basic Erlang
      1. Integers
      2. The Erlang Shell
      3. Floats
      4. Atoms
      5. Booleans
      6. Tuples
      7. Lists
      8. Term Comparison
      9. Variables
      10. Complex Data Structures
      11. Pattern Matching
      12. Functions
      13. Modules
      14. Exercises
    6. 3. Sequential Erlang
      1. Conditional Evaluations
      2. Guards
      3. Built-in Functions
      4. Recursion
      5. Runtime Errors
      6. Handling Errors
      7. Library Modules
      8. The Debugger
      9. Exercises
    7. 4. Concurrent Programming
      1. Creating Processes
      2. Message Passing
      3. Receiving Messages
      4. Registered Processes
      5. Timeouts
      6. Benchmarking
      7. Process Skeletons
      8. Tail Recursion and Memory Leaks
      9. A Case Study on Concurrency-Oriented Programming
      10. Race Conditions, Deadlocks, and Process Starvation
      11. The Process Manager
      12. Exercises
    8. 5. Process Design Patterns
      1. Client/Server Models
      2. A Process Pattern Example
      3. Finite State Machines
      4. Event Managers and Handlers
      5. Exercises
    9. 6. Process Error Handling
      1. Process Links and Exit Signals
      2. Robust Systems
      3. Exercises
    10. 7. Records and Macros
      1. Records
      2. Macros
      3. Exercises
    11. 8. Software Upgrade
      1. Upgrading Modules
      2. Behind the Scenes
      3. Upgrading Processes
      4. The .erlang File
      5. Exercise
    12. 9. More Data Types and High-Level Constructs
      1. Functional Programming for Real
      2. Funs and Higher-Order Functions
      3. List Comprehensions
      4. Binaries and Serialization
      5. References
      6. Exercises
    13. 10. ETS and Dets Tables
      1. ETS Tables
      2. Dets Tables
      3. A Mobile Subscriber Database Example
      4. Exercises
    14. 11. Distributed Programming in Erlang
      1. Distributed Systems in Erlang
      2. Distributed Computing in Erlang: The Basics
      3. The epmd Process
      4. Exercises
    15. 12. OTP Behaviors
      1. Introduction to OTP Behaviors
      2. Generic Servers
      3. Supervisors
      4. Applications
      5. Release Handling
      6. Other Behaviors and Further Reading
      7. Exercises
    16. 13. Introducing Mnesia
      1. When to Use Mnesia
      2. Configuring Mnesia
      3. Transactions
      4. Partitioned Networks
      5. Further Reading
      6. Exercises
    17. 14. GUI Programming with wxErlang
      1. wxWidgets
      2. wxErlang: An Erlang Binding for wxWidgets
      3. A First Example: MicroBlog
      4. The MiniBlog Example
      5. Obtaining and Running wxErlang
      6. Exercises
    18. 15. Socket Programming
      1. User Datagram Protocol
      2. Transmission Control Protocol
      3. The inet Module
      4. Further Reading
      5. Exercises
    19. 16. Interfacing Erlang with Other Programming Languages
      1. An Overview of Interworking
      2. Interworking with Java
      3. C Nodes
      4. Erlang from the Unix Shell: erl_call
      5. Port Programs
      6. Library Support for Communication
      7. Linked-in Drivers and the FFI
      8. Exercises
    20. 17. Trace BIFs, the dbg Tracer, and Match Specifications
      1. Introduction
      2. The Trace BIFs
      3. Tracing Calls with the trace_pattern BIF
      4. The dbg Tracer
      5. Match Specifications: The fun Syntax
      6. Match Specifications: The Nuts and Bolts
      7. Further Reading
      8. Exercises
    21. 18. Types and Documentation
      1. Types in Erlang
      2. TypEr: Success Types and Type Inference
      3. Documentation with EDoc
      4. Exercises
    22. 19. EUnit and Test-Driven Development
      1. Test-Driven Development
      2. EUnit
      3. The EUnit Infrastructure
      4. Testing State-Based Systems
      5. Testing Concurrent Programs in Erlang
      6. Exercises
    23. 20. Style and Efficiency
      1. Applications and Modules
      2. Processes and Concurrency
      3. Stylistic Conventions
      4. Coding Strategies
      5. Efficiency
      6. And Finally...
    24. A. Using Erlang
      1. Getting Started with Erlang
      2. Tools for Erlang
      3. Where to Learn More
    25. Index
    26. About the Authors
    27. Colophon
    28. SPECIAL OFFER: Upgrade this ebook with O’Reilly
O'Reilly logo

Chapter 4. Concurrent Programming

Concurrency is the ability for different functions to execute in parallel without affecting each other unless explicitly programmed to do so. Each concurrent activity in Erlang is called a process. The only way for processes to interact with each other is through message passing, where data is sent from one process to another. The philosophy behind Erlang and its concurrency model is best described by Joe Armstrong’s tenets:

  • The world is concurrent.

  • Things in the world don’t share data.

  • Things communicate with messages.

  • Things fail.

The concurrency model and its error-handling mechanisms were built into Erlang from the start. With lightweight processes, it is not unusual to have hundreds of thousands, even millions, of processes running in parallel, often with a small memory footprint. The ability of the runtime system to scale concurrency to these levels directly affects the way programs are developed, differentiating Erlang from other concurrent programming languages.

What if you were to use Erlang to write an instant messaging (IM) server, supporting the transmission of messages between thousands of users in a system such as Google Talk or Facebook? The Erlang design philosophy is to spawn a new process for every event so that the program structure directly reflects the concurrency of multiple users exchanging messages. In an IM system, an event could be a presence update, a message being sent or received, or a login request. Each process will service the event it handles, and terminate when the request has been completed.

You could do the same in C or Java, but you would struggle when scaling the system to hundreds of thousands of concurrent events. An option might be to have a pool of processes handling specific event types or particular users, but certainly not a new process for every event. Erlang gets away with this because it does not use native threads to represent processes. It has its own scheduler in the virtual machine (VM), making the creation of processes very efficient while at the same time minimizing their memory footprint. This efficiency is maintained regardless of the number of concurrent processes in the system. The same argument applies for message passing, where the time to send a message is negligible and constant, regardless of the number of processes. This chapter introduces concurrent programming in Erlang, letting you in on one of the most powerful concurrency models available today.

Creating Processes

So far, we’ve looked at executing sequential code in a single process. To run concurrent code, you have to create more processes. You do this by spawning a process using the spawn(Module, Function, Arguments) BIF. This BIF creates a new process that evaluates the Function exported from the module Module with the list of Arguments as parameters. The spawn/3 BIF returns a process identifier, which from now on we will refer to as a pid.

In Figure 4-1, the process we call Pid1 executes the spawn BIF somewhere in its program. This call results in the new process with process identifier Pid2 being created. Process identifier Pid2 is returned as a result of the call to spawn, and will typically be bound to a variable in an expression of the following format:

Pid2 = spawn(Module, Function, Arguments).
Before and after calling spawn

Figure 4-1. Before and after calling spawn

The pid of the new process, Pid2, at this point is known only within the process Pid1, as it is a local variable that has not been shared with anybody. The spawned process starts executing the exported function passed as the second argument to the BIF, and the arity of this function is dictated by the length of the list passed as the third argument to the BIF.

Warning

A common error when you start programming Erlang is to forget that the third argument to spawn is a list of arguments, so if you want to spawn the function m:f/1 with the argument a, you need to call:

spawn(m, f, [a])

not:

not spawn(m, f, a).

Once spawned, a process will continue executing and remain alive until it terminates. If there is no more code to execute, a process is said to terminate normally. On the other hand, if a runtime error such as a bad match or a case failure occurs, the process is said to terminate abnormally.

Spawning a process will never fail, even if you are spawning a nonexported or even a nonexistent function. As soon as the process is created and spawn/3 returns the pid, the newly created process will terminate with a runtime error:

1> spawn(no_module, nonexistent_function, []).
<0.32.0>

=ERROR REPORT==== 29-Feb-2008::21:48:29 ===
Error in process <0.32.0> with exit value: 
  {undef,[{no_module,nonexistent_function,[]}]}

In the preceding example, note how the error report is formatted. It is different from the ones you saw previously, as the error does not take place in the shell, but in the process with pid <0.32.0>. If the error occurs in a spawned process, it is detected by another part of the Erlang runtime system called the error logger, which by default prints an error report in the shell using the format shown earlier. Errors detected by the shell are instead formatted in a more readable form.

The processes() BIF returns a list of all of the processes running in the system. In most cases, you should have no problems using the BIF, but there have been extreme situations in large systems where calling processes() from the shell has been known to result in the runtime system running out of memory![13] Don’t forget that in industrial applications, you might be dealing with millions of processes running concurrently. In the current implementation of the runtime system, the absolute limit is in the hundreds of millions. Check the Erlang documentation for the latest figures. The default number is much lower, but you can easily change it by starting the Erlang shell with the command erl +P MaxProcceses, where MaxProcesses is an integer.

You can use the shell command i() to find out what the currently executing processes in the runtime system are doing. It will print the process identifier, the function used to spawn the process, the function in which the process is currently executing, as well as other information covered later in this chapter. Look at the example in the following shell printout. Can you spot the process that is running as the error logger?

2> processes().
[<0.0.0>,<0.2.0>,<0.4.0>,<0.5.0>,<0.7.0>,<0.8.0>,<0.9.0>,
 <0.10.0>,<0.11.0>,<0.12.0>,<0.13.0>,<0.14.0>,<0.15.0>,
 <0.17.0>,<0.18.0>,<0.19.0>,<0.20.0>,<0.21.0>,<0.22.0>,
 <0.23.0>,<0.24.0>,<0.25.0>,<0.26.0>,<0.30.0>]
3> i().
Pid                   Initial Call                          Heap     Reds Msgs
Registered            Current Function                     Stack
<0.0.0>               otp_ring0:start/2                      987     2684    0
init                  init:loop/1                              2
<0.2.0>               erlang:apply/2                        2584    61740    0
erl_prim_loader       erl_prim_loader:loop/3                   5
<0.4.0>               gen_event:init_it/6                    610      219    0
error_logger          gen_event:fetch_msg/5                   11
<0.5.0>               erlang:apply/2                        1597      508    0
...

If you are wondering why the processes() BIF returned far more than 20 processes when you created only one that failed right after being spawned, you are not alone. Large parts of the Erlang runtime system are implemented in Erlang, the error_logger and the Erlang shell being two of the many examples. You will come across other system processes as you work your way through the remaining chapters of this book.



[13] Partially because the return values of the operations in the shell are cached.

The best content for your career. Discover unlimited learning on demand for around $1/day.