1.5 The indeterministic soda machine

In addition to allowing you to focus on the scalability aspect of concurrent applications, actors' higher-level perspective on concurrency is helpful because it provides a more realistic abstraction for understanding how concurrent applications work. Specifically, concurrent programs exhibit two characteristics that, while also present in sequential applications, are especially pronounced when a program is designed from the ground up to take advantage of concurrency. To see what these are, we need only to stop by the office soda machine.[12]

A soda machine is convenient not only to provide a beverage to quench our thirst, but also because it's a good metaphor for a kind of program that moves from one well-defined state to another. To start out, a soda machine awaits input from the user, perhaps prompting the user to insert coins. Inserting those coins causes the soda machine to enter a state where it can now ask the user to make a selection of the desired drink. As soon as the user makes that selection, the soda machine dispenses a can and moves back into its initial state. On occasion, it may also run out of soda cans—that would place it in an "out of service" state.

image images/sodaMachine90.jpg

Figure 1.1 - State transitions in a soda machine.

At any point in time, a soda machine is aware of only one state. That state is also global to the machine: each component—the coin input device, the display unit, the selection entry keypad, the can dispenser, and so on—must consult that global state to determine what action to take next. For instance, if the machine is in the state where the user has already made his or her selection, the can dispenser may release a soda can into the output tray.

In addition to always being in a well-defined state, our simple abstraction suggests two further characteristics of a soda machine: First, that the number of possible states the machine can enter is finite and, second, that given any one of those possible states, we can determine in advance what the next state will be. For instance, if you inserted a sufficient amount of coins, you would expect the machine to prompt you for the choice of drink. And having made that choice, you expect the machine to dispense your selected drink.

Of course, you've probably experienced occasions when soda machines did not behave in such a predictable, deterministic way. You may have inserted plenty of coins, but instead of the machine prompting you for your choice, it delivered an unwelcoming "OUT OF ORDER" message. Or you may not have received any message at all—but also did not receive your frosty refreshment, no matter how hard you pounded the machine. Real-world experience teaches us that soda machines, like most physical objects, are not entirely deterministic. Most of the time they move from one well-defined state to another in an expected, predetermined fashion; but on occasion they move from one state to another—to an error state, for instance—in a way that you could not predict in advance.

A more realistic model of a soda machine, therefore, should include the property of some indeterminism: a model that readily admits a soda machine's ability to shift from one state to another in a way that you could not determine in advance with certainty.

Although we are generally adept at dealing with such indeterminism in physical objects—as well as when dealing with people—when we encounter such indeterminism in software, we tend to consider that behavior a bug. Examining such "bugs" may reveal that they crept into our code because some aspect of our program was not sufficiently specified.

Naturally, as developers, we desire to create programs that are well-specified and, therefore, behave as expected—programs that act exactly in accord with detailed and exhaustive specifications. Indeed, one way to provide more or less exact specifications for code is by writing tests for it.

Concurrent programs, however, are a bit more like soda machines than deterministic sequential code. Concurrent programs, unlike sequential ones, gain many of their benefits because the developer intentionally left some aspects of a concurrent system unspecified.

The reason for that is easy to understand intuitively when considering a processor with four cores: Suppose that code running on the first core sends messages to code running on the three other cores, and then awaits replies back from each. Upon receiving a reply, the first core performs further processing on the response message.

In practice, the order in which cores 2, 3, and 4 send back their replies is determined by the order in which the three cores finish their computations. If that reply order is left unspecified, then core 1 can start processing a reply as soon as it receives one; it does not have to wait for the slowest core to finish its work.

image images/messagesBetweenCores90.jpg

Figure 1.2 - Message passing with indeterministic message ordering.

In this example, leaving the reply order from cores 2, 3, and 4 unspecified helps to best utilize the available computing resources. At the same time, your program can no longer rely on any specific message ordering. Instead, your application must function deterministically even though its component computations, or how those components interact, may not be fully specified.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset