mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-08 07:42:33 +01:00
Renamed current as trunk.
git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@1433 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
[[Property:title|Baboon crossing]]
|
||||
[[Property:weight|-10]]
|
||||
[[Property:uuid|8b48b7bb-baa8-41a2-2d93-1d9667b05323]]
|
||||
|
||||
|
||||
=Description=
|
||||
|
||||
In the baboon crossing problem, a number of baboons are located on the edges of a deep canyon. There are baboons on both sides and, as you have probably guessed, some of the baboons on the left side want to get to the right side, and vice versa. Fortunately, a large rope has been stretched across the abyss allowing baboons to cross by brachiation: hanging from the rope and swinging hand-over-hand to the other side.
|
||||
|
||||
The baboon crossing policy must be compatible with the constraints of the situation:
|
||||
|
||||
# If two baboons meet in the middle of the rope, then all activity stops, and no other baboons can cross (deadlock). So, at any given time, all the baboons on the rope must be going the same direction.
|
||||
# The rope can hold only a certain number of baboons at a time. A baboon cannot be allowed on the rope if the rope is already at full capacity.
|
||||
# A continuous stream of baboons from one direction could prevent baboons wanting to go the opposite direction from ever being able to cross (unfairness, starvation).
|
||||
|
||||
|
||||
=Highlights=
|
||||
|
||||
The rope is modeled in this example by class <code>ROPE</code>, and is the primary shared resource. The fact that, at any given moment, the rope is considered unavailable to baboons wanting to cross the canyon in the direction opposite the current flow of baboons adds an interesting twist ... as does the maximum limit of baboon flesh that the rope can support. The <code>ROPE</code> class has features to manage its state:
|
||||
|
||||
: <code>capacity</code> is an integer representing the maximum number of baboons that can be supported at one time.
|
||||
: <code>baboons</code> is an integer which is the number of baboons currently traversing the rope.
|
||||
: <code>direction</code> is the current direction of baboon flow, represented as a boolean.
|
||||
: <code>changing</code> is an indicator of whether the direction of the rope is currently in the process of changing.
|
||||
: <code>is_secure</code> is a boolean indicating whether the rope is in such a state that a baboon may be allowed to mount the rope.
|
||||
|
||||
<code>ROPE</code> also includes procedures <code>mount</code> and <code>dismount</code> which a baboon uses to get on and off the rope.
|
||||
|
||||
There are two more interesting features of <code>ROPE</code>, <code>directions</code> and <code>announce</code> which will be discussed below.
|
||||
|
||||
The baboons (modeled by class <code>BABOON</code>), as they are created, are determined at random to want to go either left or right. As each baboon is created by the root class of the example, it is launched into life. For the purposes of this example, the baboon life is short and consists of four steps:
|
||||
|
||||
: 1) Announcing one's desired direction to the rope. Doing this involves the feature <code>{ROPE}.announce</code> that was mentioned above. The rope keeps a queue of these desired directions as announced by the baboons in its <code>directions</code> feature. It is by keeping this queue that the rope can make its decisions to change the direction of the flow of baboons.
|
||||
|
||||
: 2) Mounting the rope. The baboon calls its own <code>mount</code> feature (which in turn calls <code>{ROPE}.mount</code>. The baboon's <code>mount</code> procedure includes two wait conditions. One makes sure the rope is safe (<code>{ROPE}.is_secure</code>), and the second make sure that the direction of the baboons is the same as that of the baboon wanting to mount. Whenever these conditions are met, the baboon is able to grab a spot on the rope and start to cross the canyon.
|
||||
|
||||
: 3) Traversing the canyon. Once on the rope, the baboon takes a short time to make it to the other side.
|
||||
|
||||
: 4) Dismounting the rope. After crossing the canyon, the baboon gets off the rope ... and dies immediately after attaining what was apparently its only goal in life.
|
||||
|
||||
Although it was mentioned above, it bears repeating that the rope is not just a shared resource, but an active and important player in this example. Indeed, it is the rope that controls the direction of the flow of baboons across the canyon, and ensures fairness.
|
||||
|
||||
When a baboon announces himself as a candidate for crossing the canyon in a particular direction, the rope queues this information. When the rope allows a baboon to mount and cross, the desired direction of the next baboon in the directions queue is queried. If the next baboon wants to go in a different direction, then the state of the rope is set to "changing", and no more baboons are allowed on the rope until the current stream finishes crossing. When the last baboon if that stream dismounts the rope, the direction of flow is changed and the "changing" state is repealed, allowing baboons wanting to cross in the opposite direction to have a go.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
[[Property:title|Barbershop]]
|
||||
[[Property:weight|-11]]
|
||||
[[Property:uuid|3a6f929f-17a2-c9d8-1b88-0aad83db4160]]
|
||||
|
||||
=Description=
|
||||
|
||||
The [http://en.wikipedia.org/wiki/Sleeping_barber_problem barbershop], sometimes called ''the sleeping barber'', models a barbershop with one barber, one barber's chair, and a waiting room with several chairs. The alternative name of the problem comes from the fact that if there is no one waiting for a haircut, the barber flops in his chair and falls asleep.
|
||||
|
||||
Also involved are a number of shaggy-haired customers. A customer entering the barbershop looks around and assesses the situation. If all the waiting room chairs are occupied, the customer leaves the shop to return again at a time when hopefully the queue will be shorter. If there is an open chair the customer sits down and waits for the barber.
|
||||
|
||||
Once the barber has finished cutting a customer's hair, the customer leaves the shop which allows another customer to enter the shop (if all the chairs had been occupied) and the the next customer (if there is one) in the queue to get a haircut.
|
||||
|
||||
In this example, even after getting their hair cut, the customers come back to the shop until they have had their hair cut some prescribed number of times.
|
||||
|
||||
|
||||
=Highlights=
|
||||
|
||||
The example contains classes that model the barber (<code>BARBER</code>), the customers (<code>CUSTOMER</code>), and the shop with its waiting area (<code>SHOP</code>). The root class creates as separate objects the barber, the shop, and the set of customers. As each customer is created, it is launched on its lifecycle of getting haircut after haircut until haircuts are no longer needed.
|
||||
|
||||
The <code>SHOP</code> includes features <code>enter</code> and <code>leave</code>. Customers call the <code>enter</code> feature to find out if there is a chair available in the shop. Customers call <code>leave</code>, after their hair has been cut. Both of these calls are "wrapped" as separate calls in the class <code>CUSTOMER</code>.
|
||||
|
||||
A typical customer lives in this way: As long as he still needs haircuts, he repeatedly does the following steps: He tries to enter the shop. If he's unsuccessful because all the chairs are occupied, he goes off for a while (in the implementation, his processor sleeps and then comes to the end of this step). If he is able to enter the shop, then he puts himself in the queue for a haircut. Once his haircut is complete, he reduces his number of haircuts needed by one, and leaves the shop.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
[[Property:title|Counter]]
|
||||
[[Property:weight|-14]]
|
||||
[[Property:uuid|ef5e7a86-3c2d-6a55-07b5-395f30bf8f96]]
|
||||
|
||||
=Description=
|
||||
|
||||
Unlike many of the other examples, this one is not really a problem to be solved. Rather, the Counter example uses multiple instances of the simple class <code>COUNTER</code> to explore various concurrent scenarios. Each instance of <code>COUNTER</code> has a unique identifier, a current value, and a speed. A counter's speed is that time that it takes to perform a single <code>increment</code>. You will see that some of the tests start multiple counters at different speeds. Class <code>COUNTER</code> has a procedure <code>run</code> which takes an integer as an argument, and increments the counter that many times. The example's root class contains the code to create counter instances and run the various tests.
|
||||
|
||||
=Highlights=
|
||||
|
||||
There are six tests that can be called from the root procedure:
|
||||
|
||||
<code>
|
||||
make
|
||||
-- Test counters.
|
||||
do
|
||||
print ("Counter application%N")
|
||||
-- Leave one of the following lines uncommented to perform testing.
|
||||
|
||||
test_1 -- start just one counter
|
||||
-- test_2 -- start two counters
|
||||
-- test_3 -- start many counters
|
||||
-- test_4 -- start counter_1 twice
|
||||
-- test_5 -- start counter_1 with precondition
|
||||
-- test_6 -- start counter_1 separately and counter_2 non-separately
|
||||
end
|
||||
</code>
|
||||
|
||||
You can uncomment the test that you want to run and leave the rest commented. When you run the test, you can see you can watch the output in the console window as the test progresses.
|
||||
|
||||
Have a look at the source code for each test before you run it, so that you can reconcile the output you see with your expectation of the execution.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
[[Property:title|Dining philosophers]]
|
||||
[[Property:weight|-12]]
|
||||
[[Property:uuid|569f012e-7913-fbdf-7ad7-cd17d82e64aa]]
|
||||
|
||||
=Description=
|
||||
|
||||
In the [http://en.wikipedia.org/wiki/Dining_philosophers_problem dining philosophers] a number of philosophers (five, in our example) are seated at a round table. On the table are five plates of food, one in front of each philosopher, and five forks, one between each adjacent pair of plates. So each philosopher has a plate in front of him and a fork to his left and a fork to his right.
|
||||
|
||||
The philosophers spend all their time in either of only two states: they are thinking or they are eating. The philosophers may be mental giants, but apparently manual dexterity is not their strong suit. This is evidenced by the fact that in order to eat, any philosopher must be able to pick up both of the forks positioned next to his plate (which he can do, so long as neither of the philosophers next to him is currently eating). So, while eating he must have possession of both forks, and while thinking, he has put down any forks that he had previously used. Therefore, any particular philosopher has the opportunity to eat only when the two philosophers on either side of him are thinking and have made their forks available.
|
||||
|
||||
Apart from any negative consequences from the questionable sanitary practices described above, the dining philosophers can, in improperly designed solutions, encounter problems related to concurrency. For example, if all philosophers were to pick up the fork to their right and then wait for the fork to their left to become available (or vice versa), the philosophers would be caught in a [http://en.wikipedia.org/wiki/Deadlock deadlock]. If, because of a lack of fairness in the solution, some of the philosophers get stuck in thinking mode because they can never secure the two forks necessary to eat, then those philosophers so affected would suffer a condition known as [http://en.wikipedia.org/wiki/Resource_starvation resource starvation].
|
||||
|
||||
|
||||
=Highlights=
|
||||
|
||||
This example includes three classes relevant to the problem: <code>DINING_PHILOSOPHERS</code>, <code>PHILOSOPHER</code>, and <code>FORK</code>. Class <code>DINING_PHILOSOPHERS</code> sets the problem in motion by creating the forks and philosophers all typed as <code>separate</code>, and then applying the feature <code>live</code> to each philosopher after creation.
|
||||
|
||||
Class <code>PHILOSOPHER</code> models the philosophers. The totality of a philosopher's exciting activities is modeled by the feature <code>step</code>:
|
||||
|
||||
<code>
|
||||
step
|
||||
-- Perform tasks.
|
||||
do
|
||||
think
|
||||
eat (left_fork, right_fork)
|
||||
end
|
||||
</code>
|
||||
|
||||
This feature is called by <code>{PHILOSOPHER}.live</code> repeatedly until the philosopher has eaten a prescribed number of times.
|
||||
|
||||
The feature <code>think</code> requires no access to shared objects, but the feature <code>eat</code> depends upon the philosopher's ability to secure access to both of the forks adjacent to his plate. Because all forks are separate objects, each call to <code>eat</code> waits until the processors for both the left and right forks are available (in accordance with the [[Concurrent Eiffel with SCOOP#Access to shared resources|Wait rule]]).
|
||||
|
||||
Another interesting feature of this example is the feature <code>{PHILOSOPHER}.eat</code>. If you look at the text of this feature
|
||||
|
||||
<code>
|
||||
eat (l, r: separate FORK)
|
||||
-- Eat, having grabbed l and r.
|
||||
do
|
||||
io.put_string ("Philosopher " + id.out + ": taking forks%N")
|
||||
times_eaten := times_eaten + 1
|
||||
io.put_string ("Philosopher " + id.out + ": eating%N")
|
||||
io.put_string ("Philosopher " + id.out + ": putting forks back%N")
|
||||
end
|
||||
</code>
|
||||
|
||||
and you're not wearing your SCOOP glasses, this could look a little odd to you. Here is a routine that takes two arguments <code>l</code> and <code>r</code> representing the left and right forks. But then, <code>l</code> and <code>r</code> are never used in body of the routine!
|
||||
|
||||
However, with SCOOP in mind, we realize that the fork objects are shared resources to which exclusive access must be secured before a philosopher can eat. In this example, the fork object themselves don't really do anything except serve that purpose. (Take a look at the <code>FORK</code> class, and you'll see that it has no features.)
|
||||
|
||||
In real world concurrency problems, it is likely that shared resources would play a more active role than the forks of the dining philosophers, but here it's just not necessary.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
[[Property:title|Dining savages]]
|
||||
[[Property:weight|-6]]
|
||||
[[Property:uuid|ecd618f3-14f4-1a06-7f9c-be57623a9889]]
|
||||
|
||||
=Description=
|
||||
|
||||
The problem of the dining savages (an allusion to the classic dining philosophers) is based on the arguably tasteless analogy of a number of members of a primitive culture, hereinafter called the "savages", sharing a meal from a single pot. The primary abstractions are the savages themselves, a cook, and the pot. The pot contains a certain number of servings of savage sustenance (the nature of which will be left to your imagination). Each of the savages can freely remove a serving from the pot so long as the pot is not empty. So before taking a serving, a savage must check to make sure the pot is not empty. In the case in which the pot is empty, the savage must wake up the cook to refill the pot, after which the feast continue. The savages, then, can eat only when the pot is not empty, and the cook can fill the pot only when the pot is empty.
|
||||
|
||||
|
||||
=Highlights=
|
||||
|
||||
The primary shared resource here is the pot, represented by class <code>POT</code>, which is accessed for different purposes by both the savages and by the cook. <code>POT</code> has queries <code>is_empty</code> and <code>is_full</code> that can be used by savages (modeled by class <code>SAVAGE</code>) and the cook (class <code>COOK</code>). <code>POT</code> also has a feature to allow refilling of the pot. This feature is exported only to <code>COOK</code>. Another feature, this one exported only to <code>SAVAGE</code> allows the removal of a serving from the pot.
|
||||
|
||||
The cook can also be viewed as a resource shared among all the savages. Whenever a savage executes the feature that checks the pot, he must have exclusive access to both the pot and the cook. If the pot is empty then the savage uses his access to the cook to request a refill. If the pot is not empty, then the savage exits the routine, and goes on to execute the routine that removes a serving from the pot.
|
||||
|
||||
In the root class, you can adjust the number of savages, the size (in servings) of the pot, and how hungry the savages are. The hunger index indicates how many times a savage will take a serving from the pot and eat it before becoming sated. So if the pot holds 20 servings and there are 5 savages with hunger index of 4, then the pot will become empty just as the last savage takes his last serving, meaning that the pot will not require refilling. In the same scenario, if the hunger index were 10, then 50 servings total would be required, resulting in the need for the cook to be notified to refill the pot 2 times ... and 10 servings leftover ... presumably for tomorrow's breakfast.
|
||||
|
||||
The root class creates the pot, then the cook, then some number of savages. As the savages are created, their lives are launched. To occupy themselves, they repeatedly check the pot, take a serving, and eat. They give this all up once they have eaten the number of servings prescribed by their hunger index. During the check of the pot, if the pot is empty, a separate call is made to the cook requesting that the pot be refilled, and the savage goes on about the business of removing a serving from the pot. It is possible that when the savage then tries to get a serving from the pot, the pot will still be empty. In this case the precondition on <code>get_serving_from_pot</code> will cause the savage to wait until such time as the pot is no longer empty.
|
||||
|
||||
Whenever the cook is requested to refill the pot, the <code>{COOK}.cook</code> procedure is called. The procedure takes as an argument the pot which is declared of course as separate. So access to the pot must be guaranteed before <code>cook</code> can execute. The <code>cook</code> procedure has a precondition that causes it to wait if the pot is not currently empty.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
[[Property:title|Faneuil Hall]]
|
||||
[[Property:weight|-5]]
|
||||
[[Property:uuid|93132084-5eb9-c7d9-d58c-7b5c3f7508f8]]
|
||||
=Description=
|
||||
|
||||
The Faneuil Hall example is one of several examples that comes to us from Allen Downey's book ''[http://greenteapress.com/semaphores/ The Little Book of Semaphores]''. Downey credits Grant Hutchins as the originator of the example. [http://en.wikipedia.org/wiki/Faneuil_Hall Faneuil Hall] itself is an historic building in Boston which dates from 1742 an has served as a public meeting and market place.
|
||||
|
||||
The scenario in the Faneuil Hall example involves a number of immigrants waiting to have their naturalizations confirmed by a judge and receive their certificates of citizenship. Immigrants entering the Hall wait in line to check in, then they wait to take their oaths and receive their certificates of citizenship. Meanwhile, a number of spectators can also enter the building. Once the judge enters the Hall, no one else may enter the hall. Spectators may leave, but immigrants may not. Once the immigrants in the Hall have checked in, their naturalization can be confirmed by the judge. Once confirmed, the immigrants can pick up their certificates. At some point after the confirmation, the judge leaves the Hall. At that point, spectators can enter again, and immigrants can leave as soon as they have picked up their certificates. The judge will make successive trips into the hall until all the immigrants expect during the day have been confirmed.
|
||||
|
||||
|
||||
=Highlights=
|
||||
|
||||
The primary actors here are the immigrants, the judge, and the spectators, model by classes <code>IMMIGRANT</code>, <code>JUDGE</code>, and <code>SPECTATOR</code>, respectively. In addition to the actor classes, there is a class <code>HALL</code> that represents Faneuil Hall itself, and a root class that sets everything up and starts the processing. There is only one judge. But there can be many immigrants and spectators. Their numbers are limited to certain maximums specified by constants in the root class. The specific number of immigrants and spectators varies at random from one execution to the next. You can experiment with larger or smaller maximum numbers for immigrants and spectators by changing the values for the constants <code>{FANEUIL_HALL}.Max_immigrants</code> and <code>{FANEUIL_HALL}.Max_spectators</code>.
|
||||
|
||||
Although not really considered an actor here, the class <code>HALL</code> plays a critical role in synchronizing the concurrent actions of the immigrants, spectators, and the judge. <code>HALL</code> includes many status queries which, when used in preconditions in features of the other actors, constitute [[Concurrent Eiffel with SCOOP#Preconditions|uncontrolled precondition clauses]] which when false will cause the calling processor to wait until the condition becomes true. For example, consider the following status query from class <code>HALL</code>:
|
||||
|
||||
<code>
|
||||
immigrants_ready: BOOLEAN
|
||||
-- Are immigrants ready?
|
||||
do
|
||||
Result := present_immigrant_count = ready_immigrant_count
|
||||
end
|
||||
</code>
|
||||
|
||||
This query is used by the <code>JUDGE</code> when preparing to sit and administer oaths to the immigrants:
|
||||
|
||||
<code>
|
||||
take_place (a_hall: separate HALL)
|
||||
-- Prepare to confirm.
|
||||
require
|
||||
immigrants_ready: a_hall.immigrants_ready
|
||||
do
|
||||
print (out + " taking place%N")
|
||||
a_hall.sit_judge
|
||||
end
|
||||
</code>
|
||||
|
||||
The judge will take his place only when all the immigrants present have checked in and are ready to take the oath.
|
||||
|
||||
Another thing to note about this example is that immigrants and spectators obey slightly different rules when coming and going in the hall. Neither immigrants nor spectators may enter the hall if the judge is in the hall. Immigrants may not leave until the judge has left, but spectators may leave at anytime. So when you compare the <code>leave</code> features of the two classes you'll see a precondition that serves as a wait condition on <code>{IMMIGRANT}.leave</code> that is not present on <code>{SPECTATOR}.leave</code>.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[[Property:title|SCOOP examples]]
|
||||
[[Property:weight|8]]
|
||||
[[Property:uuid|75ddd9e0-3baf-655a-748f-ea8765a1d06d]]
|
||||
The examples for SCOOP that are distributed with EiffelStudio are solutions to classic and not-so-classic concurrency problems.
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
[[Property:title|Observer pattern]]
|
||||
[[Property:weight|-4]]
|
||||
[[Property:uuid|72c53c25-6fa5-6787-0762-cfa3d1c814c5]]
|
||||
|
||||
=Description=
|
||||
|
||||
The Observer pattern example should be considered a work in progress. During the development of SCOOP for EiffelStudio, Eiffel Software engineers began to think in terms of the impact that SCOOP might have on our own software. One area that emerged was the parsing of Eiffel software text during compilation. You know that Eiffel systems are composed of modules called classes. In a non-concurrent compilation process, the classes are parsed one after another. However, there is no reason why parsing could not take place on multiple, concurrently executing SCOOP processors.
|
||||
|
||||
You may remember seeing as you compile an Eiffel system, the different degrees of compilation counting down. Degree 5 is a phase of compilation that deals with parsing classes and creating an abstract syntax tree. The Observer pattern example tries to imagine concurrent Degree 5 parsing in the presence of SCOOP.
|
||||
|
||||
|
||||
{{note|You should understand that the example doesn't really parse any Eiffel code or, for that matter, involve any real code files. Rather, it just tries to show what the structure of such a concurrent parser might look like, and the parsing step just involves a short wait to simulate the time that parsing would take.}}
|
||||
|
||||
|
||||
=Highlights=
|
||||
|
||||
The name of this example is Observer pattern, but it's not a classic example of the [http://en.wikipedia.org/wiki/Observer_pattern Observer design pattern] as commonly known. But it does have elements of the observer pattern, as you will see below.
|
||||
|
||||
The important classes here are <code>DEGREE_5</code>, <code>EIFFEL_PARSER_POOL</code>, and <code>EIFFEL_PARSER</code>. <code>DEGREE_5</code> represents Eiffel compilation degree five, parsing of classes. In the example, <code>DEGREE_5</code> uses an instance of <code>EIFFEL_PARSER_POOL</code> to manage a pool of instances of <code>EIFFEL_PARSER</code> which actually do the (simulated) parsing. The <code>EIFFEL_PARSER</code>s are declared <code>separate</code> so that they can work concurrently, parsing different files.
|
||||
|
||||
When <code>DEGREE_5</code> creates the <code>EIFFEL_PARSER_POOL</code>, it provides a maximum number of parsers to be held in the pool and a function agent which the pool can use to create a new parser instance. Then when <code>DEGREE_5</code> asks the pool to parse a file, it provides references to the file itself and two procedure agents: one for pre-parse processing and one for post-parse processing.
|
||||
|
||||
The pre-parse processing agent is associated with a routine that is used to set up a parser before asking it to parse a file.
|
||||
|
||||
When an <code>EIFFEL_PARSER</code> finishes with a file, it calls the agent for post-parse processing. In this way, it notifies the instance of <code>DEGREE_5</code> that it is done with that file.
|
||||
|
||||
So, it is here that elements of the observer pattern become evident, just in a slightly non-typical way. In more typical observer pattern examples, there is one observed object and a set of one or more observers. But here, there is one observer, the instance of <code>DEGREE_5</code>, and many observable objects, the parsers. When parsers complete their work, they notify their observer (the <code>DEGREE_5</code>), that they are done by executing the routine associated with the post-parse agent. So there's another difference, too. Instead of making calls directly to the observer, the observed objects apply the agents that have been provided by the observer.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
[[Property:title|Producer-consumer]]
|
||||
[[Property:weight|-13]]
|
||||
[[Property:uuid|03739be2-e0d5-f5f0-b405-0bb75c8fee0f]]
|
||||
|
||||
=Description=
|
||||
|
||||
The [http://en.wikipedia.org/wiki/Producer-consumer_problem producer-consumer] problem is a classic software concurrency problem. The problem features one or more "producers" and one or more "consumers". All producers and consumers must share access to a "buffer" into which producers insert the products they produce, and from which consumers take the products they consume. The shared buffer is "bounded", that is, it has a maximum capacity.
|
||||
|
||||
So at any time, the buffer could be empty, precluding any consumer from withdrawing a product. Or the buffer could be full, which would mean that no producer could produce a new product until a consumer had first consumed a product, making space in the buffer. To avoid concurrency related problems, producers and consumers can access the buffer only at times when no other producer or consumer is accessing it, and only when it is in the proper state for the particular type requesting access (i. e., not empty for consumers and not full for producers).
|
||||
|
||||
=Highlights=
|
||||
|
||||
The root class of the example creates the bounded product buffer and a number of producers and consumers, all given <code>separate</code> types. It requests the producers to create a number of products, and the consumers, in the aggregate, to consume that same number of products.
|
||||
|
||||
==Separate argument rule==
|
||||
|
||||
Notice that the root class uses a feature <code>launch_producer</code> (and a corresponding feature <code>launch_consumer</code>) for instructing the producers and consumers on how many products to handle. <code>launch_producer</code> looks like this:
|
||||
|
||||
<code>
|
||||
launch_producer (a_producer: separate PRODUCER)
|
||||
-- Launch `a_producer'.
|
||||
do
|
||||
a_producer.produce (900)
|
||||
end
|
||||
</code>
|
||||
|
||||
It might occur to you that it would be easier, simpler, and clearer just to include this feature's single procedural line:
|
||||
|
||||
<code>
|
||||
a_producer.produce (900)
|
||||
</code>
|
||||
|
||||
in place of the call to <code>launch_producer</code>, and dispense with the <code>launch_producer</code> feature entirely. But that is not possible in this case.
|
||||
|
||||
The reason is that <code>a_producer.produce (900)</code> is a [[Concurrent Eiffel with SCOOP#Separate types and separate calls|separate call]] (i. e., the object attached to <code>a_producer</code> is declared of a separate type), and according to the [[Concurrent Eiffel with SCOOP#Access to shared resources|separate argument rule]], calls on a separate object are valid only when applied to an argument of the enclosing routine.
|
||||
|
||||
==Wait condition==
|
||||
|
||||
This example also shows an [[Concurrent Eiffel with SCOOP#Preconditions|uncontrolled precondition]] serving as a "wait condition". In the class <code>PRODUCER</code> we see the buffer declared as a class attribute with a <code>separate</code> type:
|
||||
|
||||
<code>
|
||||
buffer: separate BOUNDED_BUFFER [INTEGER]
|
||||
-- Shared product buffer.
|
||||
</code>
|
||||
|
||||
The feature <code>store</code> contains a precondition which ensures that the shared buffer is not full when <code>store</code> gets applied:
|
||||
|
||||
<code>
|
||||
store (a_buffer: separate BOUNDED_BUFFER [INTEGER]; an_element: INTEGER)
|
||||
-- Store `an_element' into `a_buffer'.
|
||||
require
|
||||
not a_buffer.is_full
|
||||
do
|
||||
a_buffer.put (an_element)
|
||||
ensure
|
||||
not a_buffer.is_empty
|
||||
a_buffer.count = old a_buffer.count + 1
|
||||
end
|
||||
</code>
|
||||
|
||||
The <code>store</code> routine is called by the routine <code>produce</code>, passing a reference to the <code>separate</code> attribute <code>buffer</code> like this:
|
||||
|
||||
<code>
|
||||
store (buffer, l_element)
|
||||
</code>
|
||||
|
||||
Because <code>buffer</code> is considered uncontrolled in the context of <code>produce</code>, then the precondition for <code>store</code> becomes a wait condition, rather than a correctness condition. This means that if the buffer is full, then the application of the feature <code>store</code> will wait until some consumer removes an product from the buffer. The removal of a product makes the precondition hold again, and the application of <code>store</code> can proceed.
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
[[Property:title|Quicksort]]
|
||||
[[Property:weight|-7]]
|
||||
[[Property:uuid|61632685-20ad-293b-44b6-907b15d0447a]]
|
||||
|
||||
=Description=
|
||||
|
||||
The quicksort example is a concurrent implementation of the well-known [http://en.wikipedia.org/wiki/Quicksort quicksort] sorting algorithm developed by computer scientist [http://en.wikipedia.org/wiki/C._A._R._Hoare C. A. R. Hoare]. Quicksort uses a "divide and conquer" strategy to sort a structure. It applies a basic algorithm to the structure which leads to a division of the elements into to two substructures. Then it applies the same algorithm to each of the substructures, and so on, until the whole structure is sorted. Because of the repetitive application of the same algorithm to evolving parts of the structure, the quicksort is often used in computer science classes to provide students with experience in [http://en.wikipedia.org/wiki/Recursion_(computer_science) recursive] computation.
|
||||
|
||||
In the SCOOP example, instead of recursive calls, substructures are handled (within limits) by separate [[Concurrent Eiffel with SCOOP|SCOOP processors]] running concurrently.
|
||||
|
||||
|
||||
=Highlights=
|
||||
|
||||
The quicksort example sorts a randomly generated container of integers. The set-up for this example is done in the root class. It is interactive in the sense that when you run the example, you get to to choose how many elements will be sorted (within certain limits) and you get to provide a seed for the random number generator which will be used to produce the unsorted structure.
|
||||
|
||||
The quicksort algorithm is embodied in the class <code>QUICKSORTER</code>, primarily in its routine <code>sort</code>. Instances of <code>QUICKSORTER</code> declared as <code>separate</code> are spawned to sort substructures as the algorithm progresses.
|
||||
|
||||
The structures acted upon by <code>QUICKSORTER</code> are managed in instances of class <code>DATA</code>. <code>DATA</code> is a class designed specifically to support the quicksort example.
|
||||
|
||||
When the example runs, separate <code>QUICKSORTER</code> processes are used for the recursive sorts up until a certain depth of recursion is reached. The limit is defined by the <code>NATURAL</code> constant <code>{QUICKSORTER}.max_recursion_depth</code>.
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
[[Property:title|Search-insert-delete]]
|
||||
[[Property:weight|-9]]
|
||||
[[Property:uuid|2c09ce66-f3be-1e31-eac8-f06ad3d6fc3a]]
|
||||
|
||||
=Description=
|
||||
|
||||
The Search-insert-delete example involves a shared data structure that is being accessed concurrently by three types of independent actors. ''Searchers'' access the list without changing it. So, any number of concurrent searchers can be accessing the structure safely. ''Inserters'' have the ability to add new elements to the end of the structure. Only one inserter can access the structure at any given time, but can work concurrently with any number of searchers. Lastly, ''Deleters'' can remove items from any position in the structure. As such, any deleter demands exclusive access to the structure. So, while a deleter has access to the structure neither any other deleter, any inserter, nor any searcher is allowed access.
|
||||
|
||||
|
||||
=Highlights=
|
||||
|
||||
The primary actors are modeled by classes <code>SEARCHER</code>, <code>INSERTER</code>, and <code>DELETER</code>. Additionally, some common features are abstracted into a class <code>ACTOR</code> from which the effective actor classes inherit. Each actor lives only to access the data structure one time. The access looks similar in the different actor classes, and consists of executing a procedure to start the action, then waiting a random time interval, then executing a procedure to end the action.
|
||||
|
||||
The shared data structure is modeled by the class <code>SHARED_LIST</code>. Because the point of this example is to demonstrate the different allowable types of concurrent access by the different types of actors, it should be said that features that support that goal are all that you will find in this class. In other words, <code>SHARED_LIST</code> doesn't really maintain a data structure, it only provides the features necessary to coordinate safe concurrent access by searchers, inserters, and deleters.
|
||||
|
||||
<code>SHARED_LIST</code> provides features in three feature clauses, each explicitly exported one of the types of accessors. For example, the feature clause exported to clients of type <code>SEARCHER</code> includes the query <code>can_search</code> and the procedures <code>start_search</code> and <code>end_search</code>. The features available to inserters and deleters are nearly identical. Because of the different concurrency requirements of each type of actor though, the implementation for <code>can_search</code> and <code>can_delete</code> are different. Also different are the implementations for starting and ending actions for the various actor types.
|
||||
|
||||
<code>SHARED_LIST</code> keeps certain state attributes:
|
||||
|
||||
<code>
|
||||
searchers: INTEGER_32
|
||||
-- How many searchers are there?
|
||||
|
||||
inserting: BOOLEAN
|
||||
-- Is someone inserting?
|
||||
|
||||
deleting: BOOLEAN
|
||||
-- Is someone deleting?
|
||||
</code>
|
||||
|
||||
These are used in the <code>can_search</code>, <code>can_insert</code>, and <code>can_delete</code> queries, which are in turn used in the preconditions for the corresponding <code>start_xxxx</code> features. For example, <code>start_delete</code> is constrained by a precondition <code>can_delete</code>, which is implemented like this:
|
||||
|
||||
<code>
|
||||
can_delete: BOOLEAN
|
||||
-- Can delete?
|
||||
do
|
||||
Result := not deleting and not inserting and searchers = 0
|
||||
end
|
||||
</code>
|
||||
|
||||
For the deleter calling <code>{SHARED_LIST}.start_delete</code>, the precondition clause <code>can_delete</code> is an [[Concurrent Eiffel with SCOOP#Preconditions|uncontrolled precondition]]. This means that the deleter will wait until the <code>can_delete</code> becomes true before feature application of <code>start_delete</code> occurs.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
[[Property:title|Senate bus]]
|
||||
[[Property:weight|-8]]
|
||||
[[Property:uuid|cfce3b31-bb8d-8259-a02b-73fd1495fce9]]
|
||||
|
||||
=Description=
|
||||
|
||||
According to Allen Downey in his text ''[http://greenteapress.com/semaphores/ The Little Book of Semaphores],'' the Senate bus example was inspired by the [http://www.wellesley.edu/Transportation/senate.html Senate Bus at Wellesley College]. Passengers come to a bus stop to catch the Senate bus. The bus can hold 50 passengers. When the bus stops at the bus stop, the waiting passengers board. If the bus fills up, then any passengers who cannot board, must wait until the bus shows up again. Likewise, any passenger who arrives at the stop during the time the bus is boarding also must wait until the next cycle.
|
||||
|
||||
|
||||
=Highlights=
|
||||
|
||||
The root class for this example creates the bus stop, the bus, and the passengers all typed as separate.
|
||||
|
||||
The bus stop, modeled by class <code>STATION</code> has features that can be used by the bus and by passengers. Access to these
|
||||
features is restricted to the appropriate client classes through the clients part of the feature clause. Clients of type <code>PASSENGER</code> can access <code>{STATION}.pass_enter</code>. A client of type <code>BUS</code> can access <code>{STATION}.bus_enter</code>, <code>{STATION}.pick_up</code>, and <code>{STATION}.leave</code>, as well as a status feature <code>{STATION}.bus_is_waiting</code> and two passenger queues <code>{STATION}.waiting_list</code> and <code>{STATION}.checked_in_list</code>.
|
||||
|
||||
The lifecycle of a passenger is simple: enter the bus stop. This is accomplished by making a [[Concurrent Eiffel with SCOOP#Separate types and separate calls|separate call]] to <code>{STATION}.enter</code> and passing <code>Current</code> (the passenger object itself) as an argument.
|
||||
|
||||
The lifecycle of the bus is slightly more complex: enter the bus stop, pick up passengers, leave the bus stop, wait for a short time. The bus repeats this sequence forever. The routines in class <code>BUS</code> for entering the bus stop, picking up passengers, and leaving the bus stop all accept as an argument the separate bus stop object (<code>a_station: separate STATION</code>) and make a [[Concurrent Eiffel with SCOOP#Separate types and separate calls|separate call]] to the corresponding routine in <code>STATION</code>.
|
||||
|
||||
Features of the bus stop (class <code>STATION</code>) manage the queues for waiting and checked-in passengers and whether a bus is at the bus stop. Passengers are added to the waiting queue when they arrive at the station. When the bus leaves the station, any waiting passengers are transferred to the checked-in queue. When the bus arrives at the station, the passengers on the checked-in queue are allowed to board the bus (up to the first 50 passengers, that is), and the boarding passengers are then removed from the checked-in queue.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[[Property:title|Single-element producer-consumer]]
|
||||
[[Property:weight|-15]]
|
||||
[[Property:uuid|25d3e585-0eb6-efa8-ada9-8ee596df5ada]]
|
||||
=Description=
|
||||
|
||||
The single-element producer-consumer is a simpler variant of the classic [http://en.wikipedia.org/wiki/Producer-consumer_problem producer-consumer] problem. A producer produces products, in this case integers, into a single-element inventory. The products are then consumed from inventory by a consumer. The producer, consumer, and inventory are managed by separate [[Concurrent Eiffel with SCOOP#Processors|processors]], so any access they have to one another must be synchronized through scoop mechanisms.
|
||||
|
||||
=Highlights=
|
||||
|
||||
In the single-element producer-consumer only a single producer and single consumer are created, and there is only storage allowing for a single instance of the product. So, effectively in this example, the bounded buffer of the classic producer-consumer problem has a size of one.
|
||||
|
||||
The classes modeling the different actors have obvious names: <code>PRODUCER</code>, <code>CONSUMER</code>, and <code>INVENTORY</code>. The root class of the example creates one <code>separate</code> instance of each of these, and then brings the producer and consumer to life.
|
||||
|
||||
The <code>PRODUCER</code> class supports a procedure <code>produce</code> in which a product is produced and stored in the single-element <code>INVENTORY</code>. The producer can only produce an element if the inventory is currently empty. Class <code>INVENTORY</code> exports a boolean query <code>has_item</code> which is the indicator of whether a product has been produced and is available for consumption. So <code>{PRODUCER}.produce</code> has a precondition that depends upon <code>{INVENTORY}.has_item</code> being false. Because the inventory is handled by a separate processor, this precondition is [[Concurrent Eiffel with SCOOP#Preconditions|uncontrolled]] and will cause the producer to wait until the condition is true to proceed.
|
||||
|
||||
The <code>CONSUMER</code> class works in a way that is largely the symmetrical opposite of <code>PRODUCER</code>. The consumer tries to <code>consume</code> the item from the <code>INVENTORY</code>. But this only possible if an item has been produced and is available. So <code>{CONSUMER}.consume</code> has a "wait" precondition based on the query <code>{INVENTORY}.has_item</code>.
|
||||
|
||||
So the heart of the problem is the synchronization between producer and consumer sharing a single inventory. If there's already a product in inventory, the producer cannot produce more and must wait. Only when the consumer consumes the current product can the producer produce again. For the consumer's part, if there's a product currently in inventory, then the consumer can consume that product. Otherwise, the consumer must wait until the producer produces a new product. The synchronization is handled by the SCOOP mechanism of uncontrolled (wait) preconditions on the inventory.
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user