mcad portatil
BIN
anno2/YearI/MCAD/altro/LittleBookOfSemaphores.pdf
Normal file
367
anno2/YearI/MCAD/altro/hw3sol..htm
Normal file
|
@ -0,0 +1,367 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>Homework 3 solutions</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Homework 3 solutions</h1>
|
||||
|
||||
<ol start=1 type=1>
|
||||
<li class=MsoNormal style='mso-list:l1 level1 lfo3;tab-stops:list .5in'><b>7.1.
|
||||
Busy Waiting<br>
|
||||
<br>
|
||||
</b><span style='font-weight:normal'><i>Busy waiting</i></span> is
|
||||
continuously executing (using the CPU) while waiting for something to
|
||||
happen. This is typically implemented by spinning; e.g. testing a variable
|
||||
in a tight loop until it changes. <br>
|
||||
<br>
|
||||
The alternative to busy waiting is <i>blocking</i><span style='font-style:
|
||||
normal'>, where the waiting process is suspended an other processes can
|
||||
execute while the process is waiting.<br style='mso-special-character:
|
||||
line-break'>
|
||||
<![if !supportLineBreakNewLine]><br style='mso-special-character:line-break'>
|
||||
<![endif]></span></li>
|
||||
<li class=MsoNormal style='mso-list:l1 level1 lfo3;tab-stops:list .5in'><b>7.2.
|
||||
Spinlocks<br>
|
||||
<br>
|
||||
</b><span style='font-weight:normal'>Spinlocks are not appropriate for
|
||||
uniprocessor systems because by definition only one process is executing
|
||||
at a time. As a result, no other process can ever change release the lock
|
||||
– the scheduler has to intervene and switch out the spinning process
|
||||
first. As a result, the spinning process might as well suspend itself
|
||||
instead of spinning. Or, locking should disable interrupts to prevent the scheduler
|
||||
from running, so that the process is guaranteed that it can execute the
|
||||
entire critical section.<br>
|
||||
<br>
|
||||
On a 2 CPU multiprocessor, for example, a process scheduled on processor 1
|
||||
can release the lock while a process on processor 2 is spinning on it. No
|
||||
interrupt is needed. Thus, spinlocks are not necessarily wasteful on a
|
||||
multiprocessor.</span></li>
|
||||
</ol>
|
||||
|
||||
|
||||
<p class=MsoNormal>Synchronization Problems</p>
|
||||
|
||||
<p class=MsoNormal>Notes:</p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;text-indent:-.25in;mso-list:l2 level1 lfo7;
|
||||
tab-stops:list .5in'><![if !supportLists]><span style='font-family:"Times New Roman"'>-<span
|
||||
style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]>Please use real binary semaphores, counting semaphores,
|
||||
or monitors. If you invent your own synchronization method, there is a 99% chance
|
||||
you will get it wrong. Plus it makes it very hard to grade - we look very
|
||||
closely and are more likely to find problems. If you must do this, provide an
|
||||
implementation of your construct, because otherwise we don't know if:</p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:1.0in;text-indent:-.25in;mso-list:l2 level2 lfo7;
|
||||
tab-stops:list 1.0in'><![if !supportLists]><span style='font-family:"Courier New"'>o<span
|
||||
style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]>It is possible to implement</p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:1.0in;text-indent:-.25in;mso-list:l2 level2 lfo7;
|
||||
tab-stops:list 1.0in'><![if !supportLists]><span style='font-family:"Courier New"'>o<span
|
||||
style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]>How it works</p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;text-indent:-.25in;mso-list:l2 level1 lfo7;
|
||||
tab-stops:list .5in'><![if !supportLists]><span style='font-family:"Times New Roman"'>-<span
|
||||
style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]>If you use semaphores, you must specify initial values
|
||||
for your semaphores (mandatory).</p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;text-indent:-.25in;mso-list:l2 level1 lfo7;
|
||||
tab-stops:list .5in'><![if !supportLists]><span style='font-family:"Times New Roman"'>-<span
|
||||
style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]>If you have a shared state, you should say what mutex
|
||||
protects the shared state (not mandatory)</p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;text-indent:-.25in;mso-list:l2 level1 lfo7;
|
||||
tab-stops:list .5in'><![if !supportLists]><span style='font-family:"Times New Roman"'>-<span
|
||||
style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]>With semaphores, if you wake up on one semaphore and then
|
||||
call signal() to wake up the next waiter you might wake yourself up -
|
||||
semaphores have history, so your next wait might be the one that awakes,
|
||||
because the other waiters might have been context switchted out before they
|
||||
call wait().</p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;text-indent:-.25in;mso-list:l2 level1 lfo7;
|
||||
tab-stops:list .5in'><![if !supportLists]><span style='font-family:"Times New Roman"'>-<span
|
||||
style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]>With semaphores, you shouldn't wait while holding a
|
||||
mutex, unless the routine signaling you does not need the mutex to wake you up.</p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;text-indent:-.25in;mso-list:l2 level1 lfo7;
|
||||
tab-stops:list .5in'><![if !supportLists]><span style='font-family:"Times New Roman"'>-<span
|
||||
style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]>With monitors, you don't need to grab a mutex (or,
|
||||
shudder, a condition variable) to access shared state. The monitor, by
|
||||
definition, ensures mutual exclusion.</p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;text-indent:-.25in;mso-list:l2 level1 lfo7;
|
||||
tab-stops:list .5in'><![if !supportLists]><span style='font-family:"Times New Roman"'>-<span
|
||||
style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]>With monitors, wait() doesn't take any parameters - it
|
||||
just waits until somebody then calls signal().</p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;text-indent:-.25in;mso-list:l2 level1 lfo7;
|
||||
tab-stops:list .5in'><![if !supportLists]><span style='font-family:"Times New Roman"'>-<span
|
||||
style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]>When writing synchronization code, you should try to
|
||||
minimize the number of context switches and the number of times a process is
|
||||
awoken when it doesn't need to be. Ideally, in a while() loop, a process should
|
||||
only be woken once.<br style='mso-special-character:line-break'>
|
||||
<![if !supportLineBreakNewLine]><br style='mso-special-character:line-break'>
|
||||
<![endif]></p>
|
||||
|
||||
<ol start=3 type=1>
|
||||
<li class=MsoNormal style='mso-list:l1 level1 lfo3;tab-stops:list .5in'><b>7.8.
|
||||
Sleeping-barber problem.</b><span style='font-weight:normal'> </span></li>
|
||||
</ol>
|
||||
|
||||
<h2 style='margin-left:.5in;mso-list:l0 level1 lfo10'><![if !supportLists]><span
|
||||
style='font-size:12.0pt'><span style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]><span style='font-size:12.0pt'>Barbershop Requirements:<o:p></o:p></span></h2>
|
||||
|
||||
<h3 style='margin-left:.5in;mso-list:l0 level1 lfo11'><![if !supportLists]><span
|
||||
style='font-size:10.0pt'>-<span style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]><span style='font-size:10.0pt'>Barber sleeps if no
|
||||
customers waiting<o:p></o:p></span></h3>
|
||||
|
||||
<h3 style='margin-left:.5in;mso-list:l0 level1 lfo11'><![if !supportLists]><span
|
||||
style='font-size:10.0pt'>-<span style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]><span style='font-size:10.0pt'>Customers leave if no
|
||||
chairs for waiting<o:p></o:p></span></h3>
|
||||
|
||||
<h3 style='margin-left:.5in;mso-list:l0 level1 lfo11'><![if !supportLists]><span
|
||||
style='font-size:10.0pt'>-<span style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]><span style='font-size:10.0pt'>Waiting customers can't
|
||||
leave until haircut done.<o:p></o:p></span></h3>
|
||||
|
||||
<h2 style='margin-left:.5in;mso-list:l0 level1 lfo10'><![if !supportLists]><span
|
||||
style='font-size:12.0pt'><span style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]><span style='font-size:12.0pt'>Solution: we use
|
||||
semaphores<o:p></o:p></span></h2>
|
||||
|
||||
<h3 style='margin-left:.5in;mso-list:l0 level1 lfo11'><![if !supportLists]><span
|
||||
style='font-size:10.0pt'>-<span style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]><span style='font-size:10.0pt'>Mutex = 1<o:p></o:p></span></h3>
|
||||
|
||||
<h3 style='margin-left:.5in;mso-list:l0 level1 lfo11'><![if !supportLists]><span
|
||||
style='font-size:10.0pt'>-<span style='font:7.0pt "Times New Roman"'>
|
||||
</span></span><![endif]><span style='font-size:10.0pt'>counting:
|
||||
barber_sleeping, customer_queue, cut_done<o:p></o:p></span></h3>
|
||||
|
||||
|
||||
<b>Barber Code</b><br>
|
||||
<br>
|
||||
|
||||
<pre>
|
||||
wait(mutex)
|
||||
if (customers_waiting == 0) {
|
||||
signal(mutex);
|
||||
wait(barber_sleeping);
|
||||
wait(mutex);
|
||||
}
|
||||
customers_waiting--;
|
||||
signal(mutex);
|
||||
signal(customer_queue);
|
||||
do_cut_hair();
|
||||
signal(cut_done);
|
||||
</pre>
|
||||
|
||||
<b>Customer Code</b><br>
|
||||
|
||||
<pre>
|
||||
wait(mutex);
|
||||
if (customers_waiting == n) {
|
||||
signal(mutex);
|
||||
return;
|
||||
}
|
||||
customers_waiting++;
|
||||
if (customers_waiting == 1) {
|
||||
signal(barber_sleeping);
|
||||
}
|
||||
signal(mutex);
|
||||
wait(customer_queue);
|
||||
get_hair_cut();
|
||||
wait(cut_done);
|
||||
</pre>
|
||||
|
||||
<b>As a monitor</b>
|
||||
|
||||
<pre>
|
||||
monitor barbershop {
|
||||
int num_waiting;
|
||||
condition get_cut;
|
||||
condition barber_asleep;
|
||||
condition in_chair;
|
||||
condition cut_done;
|
||||
|
||||
Barber routine
|
||||
barber() {
|
||||
while (1);
|
||||
while (num_waiting == 0) {
|
||||
barber_asleep.wait();
|
||||
}
|
||||
customer_waiting.signal();
|
||||
in_chair.wait();
|
||||
give_hait_cut();
|
||||
cut_done.signal();
|
||||
}
|
||||
Customer routine
|
||||
customer () {
|
||||
if (num_waiting == n) {
|
||||
return;
|
||||
}
|
||||
if (num_waiting == 0) {
|
||||
barber_asleep.signal();
|
||||
}
|
||||
customer_waiting.wait();
|
||||
in_char.signal();
|
||||
get_hair_cut();
|
||||
cut_done.wait();
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p style='margin-left:.5in;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><![if !supportEmptyParas]> <![endif]><o:p></o:p></p>
|
||||
|
||||
<p style='margin-left:.5in;text-indent:-.25in;mso-list:l1 level1 lfo3;
|
||||
tab-stops:list .5in'><![if !supportLists]>7.<span style='font:7.0pt "Times New Roman"'>
|
||||
</span><![endif]><b>7.9. The Cigarette-Smokers Problem.</b><span
|
||||
style='font-weight:normal'> </span></p>
|
||||
|
||||
<p style='margin-left:.5in;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'>Semaphores
|
||||
are a convenient mechanism for implementing this, because they remember what
|
||||
elements are on the table.</p>
|
||||
|
||||
<p style='margin-left:.5in;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
|
||||
style='font-family:"Courier New"'>Sempahore TobaccoAndPaper = 0;<br>
|
||||
Sempahore PaperAndMatches = 0;<br>
|
||||
Semaphore MatchesAndTobacco = 0;<br>
|
||||
Sempaphore DoneSmoking = 1;<o:p></o:p></span></p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
|
||||
style='font-family:"Courier New"'>void agent() <br>
|
||||
{<br>
|
||||
wait(DoneSmoking);<br>
|
||||
int r = rand() % 3;<o:p></o:p></span></p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
|
||||
style='font-family:"Courier New"'><br>
|
||||
//<br>
|
||||
// Signal which ever combination was<br>
|
||||
// chosen.<br>
|
||||
//<br style='mso-special-character:line-break'>
|
||||
<![if !supportLineBreakNewLine]><br style='mso-special-character:line-break'>
|
||||
<![endif]><o:p></o:p></span></p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
|
||||
style='font-family:"Courier New"'><br>
|
||||
switch( r ) {<br>
|
||||
case 0:
|
||||
signal(TobaccoAndPaper);<br>
|
||||
break;<br>
|
||||
case 1:
|
||||
signal(PaperAndMatches);<br>
|
||||
break;<br>
|
||||
case 2:
|
||||
signal(MatchesAndTobacco);<br>
|
||||
break;<br>
|
||||
}<br>
|
||||
}<br>
|
||||
<br>
|
||||
void Smoker_A()<br>
|
||||
{<br>
|
||||
while(true) {<br>
|
||||
<br>
|
||||
//<br>
|
||||
// Wait for our
|
||||
two ingredients<br>
|
||||
//<br>
|
||||
<br>
|
||||
|
||||
wait(TobaccoAndPaper);<br>
|
||||
smoke();<br>
|
||||
<br>
|
||||
//<br>
|
||||
// Signal that
|
||||
we're done smoking<br>
|
||||
// so the next
|
||||
agent can put down<br>
|
||||
// ingredients.<br>
|
||||
//<br>
|
||||
<br>
|
||||
|
||||
signal(DoneSmoking);<br>
|
||||
}<br>
|
||||
}<br style='mso-special-character:line-break'>
|
||||
<![if !supportLineBreakNewLine]><br style='mso-special-character:line-break'>
|
||||
<![endif]></span></p>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'>Smoker
|
||||
b and c are similar.<span style='font-family:"Courier New"'><o:p></o:p></span></p>
|
||||
|
||||
<p style='margin-left:.5in;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
|
||||
style="mso-spacerun: yes"> </span></p>
|
||||
|
||||
<pre style='margin-left:.5in'><span style="mso-spacerun: yes"> </span></pre>
|
||||
|
||||
<p style='margin-left:.5in;text-indent:-.25in;mso-list:l1 level1 lfo3;
|
||||
tab-stops:list .5in'><![if !supportLists]>8.<span style='font:7.0pt "Times New Roman"'>
|
||||
</span><![endif]><b>7.15. File-synchronization</b><span style='font-weight:
|
||||
normal'> </span></p>
|
||||
|
||||
<p style='margin-left:.5in;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'>
|
||||
From the book you should know how to use a <i>conditional-wait</i><span
|
||||
style='font-style:normal'> construct (priority-based signaling), so we'll use it here. We'll assume for simplicity that no
|
||||
process has an id greater than or equal to n (or we'd just print an error
|
||||
message). </span></p>
|
||||
|
||||
<pre style='margin-left:.5in'><![if !supportEmptyParas]> <![endif]><o:p></o:p></pre><pre
|
||||
style='margin-left:.5in'> type file = monitor</pre><pre style='margin-left:
|
||||
.5in'><span style="mso-spacerun: yes"> </span>var space_available: binary condition</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>total: integer</pre><pre
|
||||
style='margin-left:.5in'> </pre><pre style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>procedure entry file_open(id)</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>begin</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>while (total + id >= n)</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>space_available.wait(id)</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>total = total + id;</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>if (total < n - 1)</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>space_available.signal();</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>end</pre><pre
|
||||
style='margin-left:.5in'><![if !supportEmptyParas]> <![endif]><o:p></o:p></pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>procedure entry file_close(id)</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>begin</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>total = total - id;</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>space_available.signal();</pre><pre
|
||||
style='margin-left:.5in'><span style="mso-spacerun: yes"> </span>end</pre>
|
||||
|
||||
<p class=MsoNormal style='margin-left:.5in;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'>What
|
||||
happens here? As long as the incoming processes find adequate space available
|
||||
(i.e. sums less than n), they will claim that space and open the file. Note
|
||||
that since we're using a binary condition, we can signal() at will even if
|
||||
nothing is waiting. If a process finds inadequate space, it will block. When a
|
||||
process is done, it wakes up the process with the smallest id (the most likely
|
||||
to fit in the available space). This process, upon waking, then signals the
|
||||
next process if space is still available, but only after successfully claiming
|
||||
its own space. At some point (quite possibly with the first process), the space
|
||||
made available may not be enough for the waking process, and so a process will
|
||||
be woken up prematurely. This is what the loop is for; it will immediately
|
||||
block again. </p>
|
||||
|
||||
|
||||
|
||||
<ol start=6 type=1>
|
||||
<li class=MsoNormal style='mso-list:l1 level1 lfo3;tab-stops:list .5in'><b>8.4
|
||||
b<br>
|
||||
</b>install traffic lights :)
|
||||
<br>
|
||||
<li class=MsoNormal style='mso-list:l1 level1 lfo3;tab-stops:list .5in'><b>8.8
|
||||
<br>
|
||||
</b>The system can always make progress in all possible
|
||||
allocation scenarios (use pigeonhole principle to show this).
|
||||
</ol>
|
||||
<hr>
|
||||
</body>
|
||||
</html>
|
406
anno2/YearI/MCAD/bardellostadio.md
Normal file
|
@ -0,0 +1,406 @@
|
|||
# Semafori
|
||||
|
||||
## Inizializzazione
|
||||
|
||||
```
|
||||
semaphore mutex = 1;
|
||||
semaphore ospiti, locali, pulizia; // iniz. a 0
|
||||
int nOsp = 0;
|
||||
int nLoc = 0;
|
||||
int attesaOspiti = 0;
|
||||
int attesaLocali = 0;
|
||||
bool chiusura;
|
||||
```
|
||||
## Ospiti
|
||||
|
||||
```
|
||||
public void entraOspite () {
|
||||
P (mutex);
|
||||
if (nOsp == NMAX || chiusura) {
|
||||
V (mutex);
|
||||
attesaOspiti++;
|
||||
P (ospiti);
|
||||
attesaOspiti--;
|
||||
}
|
||||
nOsp++;
|
||||
if (nOsp < NMAX && !chiusura && attesaOspiti > 0) {
|
||||
V (ospiti);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
public void esciOspite () {
|
||||
P (mutex);
|
||||
nOsp--;
|
||||
if (attesaOspiti > 0 && !chiusura) {
|
||||
V (ospiti);
|
||||
} else if (attesaOspiti == 0 && attesaLocali > 0 && nOsp == 0 && !chiusura) {
|
||||
V (locali);
|
||||
} else if (nOsp == 0 && chiusura) {
|
||||
V (pulizia);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Locali
|
||||
|
||||
```
|
||||
public void entraLocale () {
|
||||
P (mutex);
|
||||
if (chiusura || nLoc == NMAX || (attesaOspiti > 0 && nLoc == 0)) {
|
||||
V (mutex);
|
||||
attesaLocali++;
|
||||
P (locali);
|
||||
attesaLocali--;
|
||||
}
|
||||
nLoc++;
|
||||
if (nLoc < NMAX && !chiusura && attesaLocali > 0) {
|
||||
V (locali);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
public void esciLocale () {
|
||||
P (mutex);
|
||||
nLoc--;
|
||||
|
||||
if (nLoc == 0 && !chiusura && attesaOspiti > 0) {
|
||||
V (ospiti);
|
||||
} else if (attesaLocali > 0 && !chiusura) {
|
||||
V (locali);
|
||||
} else if (nLoc == 0 && chiusura) {
|
||||
V (pulizia);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
## Barista
|
||||
|
||||
```
|
||||
public void pulisciBar () {
|
||||
P (mutex);
|
||||
chiusura = true;
|
||||
if (nLoc > 0 || nOsp > 0) {
|
||||
V (mutex);
|
||||
P (pulizia);
|
||||
}
|
||||
<...pulisci...>
|
||||
chiusura = false;
|
||||
|
||||
if (attesaOspiti > 0) {
|
||||
V (ospiti);
|
||||
} else if (attesaLocali > 0) {
|
||||
V (locali);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Monitor
|
||||
|
||||
```
|
||||
monitor bar {
|
||||
int nLoc = 0;
|
||||
int nOsp = 0;
|
||||
bool chiusura = false;
|
||||
condition locali, ospiti, pulizia;
|
||||
|
||||
public void entraOspite() {
|
||||
if (nOsp == NMAX || chiusura) {
|
||||
wait (ospiti);
|
||||
}
|
||||
nOsp++;
|
||||
if (nOsp < NMAX && !chiusura && !empty(ospiti)) {
|
||||
signal (ospiti);
|
||||
}
|
||||
}
|
||||
|
||||
public void esciOspite () {
|
||||
nOsp--;
|
||||
|
||||
if (!empty(ospiti) > 0 && !chiusura) {
|
||||
signal (ospiti);
|
||||
} else if (nOsp == 0 && empty(ospiti) == 0 && !empty (locali) > 0 && !chiusura) {
|
||||
signal (locali);
|
||||
} else if (nOsp == 0 && chiusura) {
|
||||
signal (pulizia);
|
||||
}
|
||||
}
|
||||
|
||||
public void entraLocale () {
|
||||
if (nLoc == NMAX || chiusura || (!empty(attesaOspiti) && nLoc == 0)) {
|
||||
wait (locali);
|
||||
}
|
||||
nLoc++;
|
||||
if (nLoc < NMAX && !chiusura && !empty (locali)) {
|
||||
signal (locali);
|
||||
}
|
||||
}
|
||||
|
||||
public void esciLocale () {
|
||||
nLoc--;
|
||||
|
||||
if (!empty (ospiti) && nLoc == 0 && !chiusura) {
|
||||
signal (ospiti);
|
||||
} else if (!empty (locali) && empty (ospiti) && !chiusura) {
|
||||
signal (locali);
|
||||
} else if (nLoc == 0 && chiusura) {
|
||||
signal (pulizia);
|
||||
}
|
||||
}
|
||||
|
||||
public void pulisciBar () {
|
||||
chiusura = true;
|
||||
if (nLoc > 0 || nOsp > 0) {
|
||||
wait (pulizia);
|
||||
}
|
||||
<...pulisci...>
|
||||
chiusura = false;
|
||||
if (!empty(ospiti) {
|
||||
signal (ospiti);
|
||||
} else if (!empty (locali)) {
|
||||
signal (locali);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
# Scambio di messaggi asincrono
|
||||
|
||||
Implementazione in scambio di messaggi **asincrono** del problema "il bar dello stadio".
|
||||
Seguono le implementazioni del processo gestore e le interfacce Barista, Locali, Ospiti.
|
||||
|
||||
## Gestore
|
||||
|
||||
```
|
||||
process gestoreBar {
|
||||
port signal entraOsp, entraLoc, esciOsp, esciLoc;
|
||||
port signal richiestaPulizia, finePulizia;
|
||||
process barista, locale, ospite;
|
||||
int nLoc = 0;
|
||||
int nOsp = 0;
|
||||
bool chiusura = false;
|
||||
signal s;
|
||||
queue attesaOsp, attesaLoc; // inizializzate come empty
|
||||
|
||||
// helper function per svegliare in maniera corretta i processi in coda
|
||||
public void svegliaProcesso () {
|
||||
|
||||
if (chiusura && nOsp == 0 && nLoc == 0) {
|
||||
send (s) to barista.ok_pulizia;
|
||||
barista = receive (s) from finePulizia;
|
||||
|
||||
} else if (!chiusura && !attesaOsp.empty && nLoc == 0) {
|
||||
while (!attesaOsp.empty && nOsp < NMAX) {
|
||||
ospite = attesaOsp.dequeue();
|
||||
send (s) to ospite.ok_entra;
|
||||
nOsp++;
|
||||
}
|
||||
|
||||
} else if (!chiusura && !attesaLoc.empty && nOsp == 0 && attesaOsp.empty) {
|
||||
while (!attesaLoc.empty && nLoc < NMAX) {
|
||||
locale = attesaLoc.dequeue();
|
||||
send (s) to locale.ok_entra;
|
||||
nLoc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
...
|
||||
// implementazione con guardie logiche
|
||||
do {
|
||||
* (nLoc == 0 && !chiusura && nOsp < NMAX); ospite = receive(s) from entraOsp; ->
|
||||
send(s) to ospite.ok_entra;
|
||||
nOsp++;
|
||||
|
||||
* (nOsp == 0 && !chiusura && nLoc < NMAX); locale = receive(s) from entraLoc; ->
|
||||
send(s) to ospite.ok_entra;
|
||||
nLoc++;
|
||||
|
||||
* (chiusura || (nOsp == NMAX && nLoc == 0)); ospite = receive(s) from entraOsp; ->
|
||||
attesaOsp.enqueue(ospite)
|
||||
|
||||
* (chiusura || (nLoc == NMAX && nOsp == 0)); locale = receive(s) from entraLoc; ->
|
||||
attesaLoc.enqueue(locale)
|
||||
|
||||
* barista = receive(s) from richiestaPulizia; ->
|
||||
chiusura = true;
|
||||
if (nLoc == 0 && nOsp == 0) {
|
||||
send (s) to barista.ok_pulizia;
|
||||
// qui pulisce
|
||||
barista = receive(s) from finePulizia;
|
||||
chiusura = false;
|
||||
|
||||
svegliaProcesso();
|
||||
}
|
||||
|
||||
* ospite = receive(s) from esciOsp; ->
|
||||
nOsp--;
|
||||
svegliaProcesso();
|
||||
|
||||
* locale = receive(s) from esciLoc; ->
|
||||
nLoc--;
|
||||
svegliaProcesso();
|
||||
} od
|
||||
```
|
||||
|
||||
## Barista
|
||||
|
||||
```
|
||||
process Barista {
|
||||
port signal ok_pulizia;
|
||||
signal s;
|
||||
process p;
|
||||
...
|
||||
send (s) to gestoreBar.richiestaPulizia;
|
||||
p = receive(s) from ok_pulizia;
|
||||
<...pulisci...>
|
||||
send (s) to gestoreBar.finePulizia;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Ospiti
|
||||
|
||||
```
|
||||
process Ospite {
|
||||
port signal ok_entra;
|
||||
signal s;
|
||||
process p;
|
||||
...
|
||||
send (s) to gestoreBar.entraOsp;
|
||||
p = receive(s) from ok_entra;
|
||||
<...bevi birra come un vero tifoso in trasferta...>
|
||||
send (s) to gestoreBar.esciOsp;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Locali
|
||||
|
||||
```
|
||||
process Locale {
|
||||
port signal ok_entra;
|
||||
signal s;
|
||||
process p;
|
||||
...
|
||||
send (s) to gestoreBar.entraLoc;
|
||||
p = receive(s) from ok_entra;
|
||||
<...bevi birra come un vero tifoso in casa...>
|
||||
send (s) to gestoreBar.esciLoc;
|
||||
...
|
||||
}
|
||||
```
|
||||
# Rendez-vous
|
||||
|
||||
```
|
||||
process bar {
|
||||
entry entraOspite;
|
||||
entry ok_entraOspite;
|
||||
entry entraLocale;
|
||||
entry ok_entraLocale;
|
||||
entry esciOspite;
|
||||
entry esciLocale;
|
||||
entry pulizia;
|
||||
entry ok_pulizia;
|
||||
entry fine_pulizia;
|
||||
int nLoc = 0;
|
||||
int nOsp = 0;
|
||||
int attesaLoc = 0;
|
||||
int attesaOsp = 0;
|
||||
bool chiusura = false;
|
||||
|
||||
do {
|
||||
* (!chiusura); accept entraOspite {}; ->
|
||||
if (nOsp == NMAX) {
|
||||
attesaOsp++;
|
||||
} else {
|
||||
accept ok_entraOspite;
|
||||
nOsp++;
|
||||
}
|
||||
|
||||
* accept esciOspite {}; ->
|
||||
nOsp--;
|
||||
if (attesaOsp > 0 && !chiusura) {
|
||||
while (attesaOsp > 0 && nOsp < NMAX) {
|
||||
accept ok_entraOspite;
|
||||
}
|
||||
} else if (attesaOsp == 0 && !chiusura && nOsp == 0 && attesaLoc > 0) {
|
||||
while (attesaLoc > 0 && nLoc < NMAX) {
|
||||
accept ok_entraLocale;
|
||||
}
|
||||
} else if (chiusura && nOsp == 0) {
|
||||
accept ok_pulizia;
|
||||
}
|
||||
|
||||
* (!chiusura); accept entraLocale {}; ->
|
||||
if (nLoc == NMAX || (attesaOsp > 0 && nLoc == 0)) {
|
||||
attesaLoc++;
|
||||
} else {
|
||||
accept ok_entraLocale;
|
||||
nLoc++;
|
||||
}
|
||||
|
||||
* accept esciLocale {}; ->
|
||||
nLoc--;
|
||||
if (nLoc == 0 && attesaOsp > 0 && !chiusura) {
|
||||
while (attesaOsp > 0 && nOsp < NMAX) {
|
||||
accept ok_entraOspite;
|
||||
}
|
||||
} else if (attesaLoc > 0 && !chiusura) {
|
||||
while (attesaLoc > 0 && nLoc < NMAX) {
|
||||
accept ok_entraLocale;
|
||||
}
|
||||
} else if (chiusura && nLoc == 0) {
|
||||
accept ok_pulizia;
|
||||
}
|
||||
|
||||
* accept pulizia {}; ->
|
||||
chiusura = true;
|
||||
if (nLoc == 0 && nOsp == 0) {
|
||||
accept ok_pulizia;
|
||||
}
|
||||
|
||||
* accept fine_pulizia {}; ->
|
||||
chiusura = false;
|
||||
} od
|
||||
}
|
||||
```
|
||||
## Ospiti
|
||||
|
||||
```
|
||||
process ospite {
|
||||
...
|
||||
call bar.entraOspite;
|
||||
call bar.ok_entraOspite;
|
||||
... bevi ...
|
||||
call bar.esciOspite;
|
||||
}
|
||||
```
|
||||
## Locali
|
||||
```
|
||||
process locale {
|
||||
...
|
||||
call bar.entraLocale;
|
||||
call bar.ok_entraLocale;
|
||||
... bevi ...
|
||||
call bar.esciLocale;
|
||||
}
|
||||
```
|
||||
## Barista
|
||||
```
|
||||
process barista {
|
||||
...
|
||||
call bar.pulizia;
|
||||
call bar.ok_pulizia;
|
||||
... pulisci ...
|
||||
call bar.fine_pulizia;
|
||||
}
|
||||
```
|
BIN
anno2/YearI/MCAD/bardellostadio.traccia.doc
Normal file
BIN
anno2/YearI/MCAD/bardellostadio.traccia.pdf
Normal file
97
anno2/YearI/MCAD/caffe.md
Normal file
|
@ -0,0 +1,97 @@
|
|||
# Miscelatura di Caffe'
|
||||
|
||||
## Inizializzazione
|
||||
|
||||
```
|
||||
sem mutex = 1;
|
||||
sem arabica = 0, robusta = 0;
|
||||
sem estrai = 0;
|
||||
const int maxArabica = 2*MAX/3
|
||||
const int maxRobusta = MAX-maxArabica;
|
||||
const int sacco = C;
|
||||
int cntArabica = 0;
|
||||
int cntRobusta = 0;
|
||||
int attesaArabica = 0;
|
||||
int attesaRobusta = 0;
|
||||
int attesaEstrattori = 0;
|
||||
```
|
||||
|
||||
## Operai versatori
|
||||
|
||||
### Versatori di Arabica
|
||||
|
||||
```
|
||||
public void versaArabica () {
|
||||
P (mutex);
|
||||
if (cntArabica == maxArabica) {
|
||||
V(mutex);
|
||||
attesaArabica++;
|
||||
P (arabica);
|
||||
attesaArabica--;
|
||||
}
|
||||
...versa...
|
||||
cntArabica++;
|
||||
|
||||
if (attesaArabica > 0 && cntArabica < maxArabica) {
|
||||
V(arabica);
|
||||
} else if (attesaRobusta > 0 && cntRobusta < maxRobusta) {
|
||||
V(robusta);
|
||||
} else if (cntRobusta == 2*cntArabica && (cntArabica +cntRobusta > sacco) && attesaEstrattori > 0) {
|
||||
V(estrai);
|
||||
} else {
|
||||
V(mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Versatori di Robusta
|
||||
|
||||
```
|
||||
public void versaRobusta () {
|
||||
P (mutex);
|
||||
if ((attesaArabica > 0 && cntArabica < maxArabica) || cntRobusta == maxRobusta) {
|
||||
V(mutex);
|
||||
attesaRobusta++;
|
||||
P (robusta);
|
||||
attesaRobusta--;
|
||||
}
|
||||
...versa...
|
||||
cntRobusta++;
|
||||
|
||||
if (attesaArabica > 0 && cntArabica < maxArabica) {
|
||||
V(arabica);
|
||||
} else if (attesaRobusta > 0 && cntRobusta < maxRobusta) {
|
||||
V(robusta);
|
||||
} else if (cntRobusta == 2*cntArabica && (cntArabica +cntRobusta > sacco) && attesaEstrattori > 0) {
|
||||
V(estrai);
|
||||
} else {
|
||||
V(mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
## Operai insaccatori
|
||||
|
||||
```
|
||||
public void insaccaMiscela () {
|
||||
P (mutex);
|
||||
if (!(cntRobusta == 2*cntArabica && (cntArabica +cntRobusta > sacco))) {
|
||||
V (mutex);
|
||||
attesaEstrattori++;
|
||||
P (estrai);
|
||||
attesaEstrattori--;
|
||||
}
|
||||
<...estrai...>
|
||||
cntRobusta = cntRobusta - sacco/3;
|
||||
cntArabica = cntArabica - 2*sacco/3;
|
||||
|
||||
if (attesaArabica > 0) {
|
||||
V(arabica);
|
||||
} else if (attesaRobusta > 0) {
|
||||
V(robusta);
|
||||
} else if ((cntArabica + cntRobusta > sacco) && attesaEstrattori > 0) {
|
||||
V(estrai);
|
||||
} else {
|
||||
V(mutex);
|
||||
}
|
||||
}
|
||||
```
|
BIN
anno2/YearI/MCAD/esercizi2018/esercizi monitor.odp
Normal file
BIN
anno2/YearI/MCAD/esercizi2018/esercizi monitor.pdf
Normal file
BIN
anno2/YearI/MCAD/esercizi2019/Esercizi1.18.doc
Normal file
BIN
anno2/YearI/MCAD/esercizi2019/bombolini.19.pdf
Normal file
BIN
anno2/YearI/MCAD/esercizi2019/bomboloni_sol/all.zip
Normal file
|
@ -0,0 +1,119 @@
|
|||
process vassoio{
|
||||
int b = 0;
|
||||
port String piero_rosa;
|
||||
String mess;
|
||||
port signal rilascio_vassoio;
|
||||
signal s;
|
||||
process p;
|
||||
boolean attesaPiero = false;
|
||||
|
||||
send(b) to mario.friggi;
|
||||
p = receive(s) from rilascio_vassoio;
|
||||
b = 30;
|
||||
while(true){
|
||||
p = receive(mess) from piero_rosa;
|
||||
if(mess=="piero"){
|
||||
b--;
|
||||
if(b==0){
|
||||
attesaPiero = true;
|
||||
send("finiti") to p.servi;
|
||||
send(0) to clock.setTimer;
|
||||
}
|
||||
else{
|
||||
send("non finiti") to p.servi;
|
||||
}
|
||||
}
|
||||
else if(mess=="rosa"){
|
||||
send(b) to mario.friggi;
|
||||
p = receive(s) from rilascio_vassoio;
|
||||
b = 30;
|
||||
send(s) to rosa.rilascio_rosa;
|
||||
if(attesaPiero){
|
||||
attesaPiero = false;
|
||||
send(s) to Piero.riprendi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Mario{
|
||||
process p;
|
||||
signal s;
|
||||
port int friggi;
|
||||
int b_rim;
|
||||
while(true){
|
||||
p = receive(b_rim) from friggi;
|
||||
if(b_rim>0)
|
||||
<butta>
|
||||
<friggi>
|
||||
send(60) to clock.setTimer;
|
||||
send(s) to p.rilascio_vassoio;
|
||||
}
|
||||
}
|
||||
|
||||
process Piero{
|
||||
process p;
|
||||
port signal riprendi;
|
||||
signal s;
|
||||
port String servi;
|
||||
String mess;
|
||||
while(true){
|
||||
<attendi clienti>
|
||||
send("piero") to vassoio.piero_rosa;
|
||||
p = receive(mess) from servi;
|
||||
<servo cliente>
|
||||
if(mess == "finiti"){
|
||||
p = receive(s) from riprendi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Rosa{
|
||||
port signal rilascio_rosa;
|
||||
signal s;
|
||||
process p;
|
||||
while(true){
|
||||
send(s) to clock.waitTimer;
|
||||
p = receive(s) from rilascio_rosa;
|
||||
send("rosa") to vassoio.piero_rosa;
|
||||
p = receive(s) from rilascio_rosa;
|
||||
}
|
||||
}
|
||||
|
||||
process Clock{
|
||||
port Signal waitTimer;
|
||||
port int setTimer;
|
||||
port Signal tick;
|
||||
signal s;
|
||||
process p;
|
||||
int min;
|
||||
int tempo = 0;
|
||||
int sveglia = 0;
|
||||
boolean inAscolto = false;
|
||||
do
|
||||
[] p = receive(s) from tick; -> // Aumenta il tempo e se il timer scade..
|
||||
tempo++;
|
||||
if(sveglia == tempo && inAscolto){ // ..se il tempo è uguale alla sveglia e Rosa è sulla receive...
|
||||
inAscolto = false;
|
||||
send(s) to rosa.rilascio_rosa; // ..allora sveglia Rosa
|
||||
sveglia = 0;
|
||||
}
|
||||
[] p = receive(min) from setTimer; -> // Viene settato il Timer a zero o a 60
|
||||
sveglia = tempo + min;
|
||||
if(sveglia == tempo && inAscolto){ // Se il tempo è uguale alla sveglia e Rosa è sulla receive...
|
||||
inAscolto = false;
|
||||
send(s) to rose.rilascio_rosa; // ..llora sveglia Rosa
|
||||
sveglia = 0;
|
||||
}
|
||||
|
||||
[] p = receive(s) from waitTimer; -> // Si mette Rosa sulla receive accettando la send di waitTimer
|
||||
inAscolto = true;
|
||||
od
|
||||
}
|
||||
process Tempo{
|
||||
signal s;
|
||||
while(true){
|
||||
<aspetta un secondo>
|
||||
send(s) to clock.tick;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
process vassoio{
|
||||
int b = 0;
|
||||
port String piero_rosa;
|
||||
String mess;
|
||||
port signal rilascio_vassoio;
|
||||
signal s;
|
||||
boolean attesaPiero = false;
|
||||
process pMario;
|
||||
process pPieroRosa;
|
||||
|
||||
send(b) to mario.friggi; // Chiede a Mario di friggere
|
||||
pMario = receive(s) from rilascio_vassoio; // Aspetta che frigga
|
||||
b = 30;
|
||||
while(true){
|
||||
pPieroRosa = receive(mess) from piero_rosa; // Aspetta i messaggi da Piero o da Rosa
|
||||
if(mess=="piero"){
|
||||
b--;
|
||||
if(b==0){ // Se era l'ultimo bombolone..
|
||||
attesaPiero = true;
|
||||
send("finiti") to pPieroRosa.servi; // ..dice a Piero che sono finiti
|
||||
setTimer(0); // e va a svegliare Rosa
|
||||
}
|
||||
else{
|
||||
send("non finiti") to pPieroRosa.servi; // altrimenti dice a Piero che ci sono ancora bomboloni
|
||||
}
|
||||
}
|
||||
else if(mess=="rosa"){
|
||||
send(b) to mario.friggi; // Chiede a Mario di friggere
|
||||
pMario = receive(s) from rilascio_vassoio; // Aspetta che frigga
|
||||
b = 30;
|
||||
send(s) to rosa.rilascio_rosa; // Avverte Rosa che Mario ha fritto
|
||||
if(attesaPiero) { // Se Piero ha venduto l'ultimo ed era in attesa..
|
||||
attesaPiero = false;
|
||||
send(s) to piero.riprendi; // ..lo sveglia
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Mario{
|
||||
process p;
|
||||
signal s;
|
||||
port int friggi;
|
||||
int b_rim;
|
||||
|
||||
while(true){
|
||||
p = receive(b_rim) from friggi; // Aspetta di essere fritto dal vassoio
|
||||
if(b_rim>0){ // Se c'erano bomboloni scaduti, li butta
|
||||
<butta>
|
||||
}
|
||||
<friggi>
|
||||
setTimer(60);
|
||||
send(s) to p.rilascio_vassoio; // Comunica al vassoio che è fritto
|
||||
}
|
||||
}
|
||||
|
||||
process Piero{
|
||||
process p;
|
||||
port signal riprendi;
|
||||
signal s;
|
||||
port String servi;
|
||||
String mess;
|
||||
|
||||
while(true){
|
||||
<attendi clienti>
|
||||
send("piero") to vassoio.piero_rosa; // Comunica al vassoio che deve servire un cliente
|
||||
p = receive(mess) from servi; // Aspetta di poter servire
|
||||
<servo cliente>
|
||||
if(mess == "finiti"){ // Se il vassoio comunica che i bomboloni sono finiti..
|
||||
p = receive(s) from riprendi; // ..si mette in attesa di poter riprendere
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Rosa{
|
||||
port signal rilascio_rosa;
|
||||
signal s;
|
||||
process p;
|
||||
|
||||
while(true){
|
||||
waitTimer();
|
||||
send("rosa") to vassoio.piero_rosa; // Avverte il vassoio di far friggere Mario
|
||||
p = receive(s) from rilascio_rosa; // Aspetta che sia fritto
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
monitor bomboloni{
|
||||
int b = 0
|
||||
conditionition mario;
|
||||
condition piero;
|
||||
condition cliente;
|
||||
condition rosa;
|
||||
boolean deveFriggere = false;
|
||||
|
||||
public void mario(){
|
||||
if (!deveFriggere) wait(mario); // Si blocca in attesa che Rosa lo sblocchi per friggere
|
||||
if(b>0) <butta i bomboloni> // Se c'erano bomboloni scaduti, li butta
|
||||
<Friggi bomboloni>
|
||||
deveFriggere = false;
|
||||
b=30;
|
||||
setTimer(60);
|
||||
signal(piero); // Sblocca Piero
|
||||
if(!empty(rosa)){ // Se Rosa prende il monitor prima di Mario resterà in attesa e..
|
||||
signal(rosa); // ..verrà svegliata dopo che Mario ha fritto
|
||||
}
|
||||
}
|
||||
|
||||
public void piero(){
|
||||
if (b == 0){ // Quando finiscono i bomboloni..
|
||||
setTimer(0); // ..sveglia Rosa
|
||||
wait(piero); // e si mette in attesa
|
||||
}
|
||||
wait(cliente);
|
||||
b--;
|
||||
}
|
||||
|
||||
public void rosa(){
|
||||
deveFriggere = true; // Se Rosa viene svegliata dobbiamo sicuramente friggere
|
||||
if(!empty(mario)){ // Se Mario è già arrivato, lo sveglia
|
||||
signal(mario);
|
||||
}
|
||||
else{
|
||||
wait(rosa); // Altrimenti si mette in attesa che Mario arrivi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bomboloni alpha;
|
||||
process piero(){
|
||||
while(true){
|
||||
alpha.piero;
|
||||
<Servi>
|
||||
}
|
||||
}
|
||||
|
||||
process mario(){
|
||||
while(true){
|
||||
alpha.mario;
|
||||
}
|
||||
}
|
||||
|
||||
process rosa(){
|
||||
while(true){
|
||||
waitTimer()
|
||||
alpha.rosa;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
process vassoio{
|
||||
int b = 0;
|
||||
entry piero_rosa(in String inMess, out String outMess);
|
||||
boolean attesaPiero = false;
|
||||
|
||||
call mario.friggi(b); // Chiama Mario per friggere
|
||||
b = 30;
|
||||
while(true){
|
||||
accept piero_rosa(in String inMess, out String outMess) // Aspetta una richiesta da Piero o da Rosa
|
||||
{
|
||||
if(inMess=="piero"){
|
||||
b--;
|
||||
if(b==0){ // Se era l'ultimo bombolone..
|
||||
attesaPiero = true;
|
||||
outMess = "finiti"; // setta l'output per dire a Piero che sono finiti
|
||||
setTimer(0); // e va a svegliare Rosa
|
||||
}
|
||||
else{
|
||||
outMess = "non finiti"; // setta l'output per dire a Piero che non sono finiti
|
||||
}
|
||||
}
|
||||
else if(inMess=="rosa"){
|
||||
call mario.friggi(b); // Chiede a Mario di friggere
|
||||
b = 30;
|
||||
if (attesaPiero) { // Se Piero ha venduto l'ultimo ed era in attesa..
|
||||
attesaPiero = false;
|
||||
call piero.riprendi(); // ..lo sveglia
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Mario{
|
||||
entry friggi(in int b_rim);
|
||||
|
||||
while(true){
|
||||
accept friggi(in int b_rim) // Aspetta di essere fritto dal vassoio
|
||||
{
|
||||
if(b_rim>0){ // Se c'erano bomboloni scaduti, li butta
|
||||
<butta>
|
||||
}
|
||||
<friggi>
|
||||
setTimer(60);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Piero{
|
||||
entry riprendi();
|
||||
String mess;
|
||||
|
||||
while(true){
|
||||
<attendi clienti>
|
||||
call vassoio.piero_rosa("piero"); // Comunica al vassoio che deve servire un cliente
|
||||
<servo cliente>
|
||||
if (mess == "finiti") { / Se il vassoio comunica che i bomboloni sono finiti..
|
||||
accept riprendi() // ..si mette in attesa di poter riprendere
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Rosa{
|
||||
while(true){
|
||||
waitTimer();
|
||||
call vassoio.piero_rosa("rosa"); // Avverte il vassoio di far friggere Mario
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
semaphoreaphore mario = 0;
|
||||
semaphore cliente = 0;
|
||||
semaphore mutex = 1;
|
||||
semaphore rosa = 0;
|
||||
semaphore piero = 0;
|
||||
int b = 0;
|
||||
boolean attesaPiero = false;
|
||||
|
||||
process mario(){
|
||||
<friggi>
|
||||
b=30;
|
||||
setTimer(60);
|
||||
V(piero); // Serve a Piero per partire
|
||||
while(true){
|
||||
P(mario); // Si blocca in attesa che Rosa lo sblocchi per friggere
|
||||
if(b>0) <Butta> // Se c'erano bomboloni scaduti, li butta
|
||||
<Friggi bomboloni>
|
||||
b=30;
|
||||
setTimer(60);
|
||||
if(attesaPiero){ // Se erano finiti i bomboloni dobbiamo sbloccare Piero con la V(piero)
|
||||
attesaPiero = false;
|
||||
V(piero);
|
||||
}
|
||||
V(rosa); // Sblocchiamo Rosa
|
||||
}
|
||||
}
|
||||
|
||||
process piero(){
|
||||
P(piero); // Blocca Piero per la prima esecuzione. Viene sbloccato da Mario
|
||||
while(true){
|
||||
P(cliente); // Attende il cliente
|
||||
P(mutex); // Per la mutua esclusione con Rosa
|
||||
<Servi>
|
||||
b--;
|
||||
if(b==0){ // Quando finiscono i bomboloni..
|
||||
attesaPiero = true;
|
||||
setTimer(0); // ..sveglia Rosa
|
||||
V(mutex);
|
||||
P(piero); // e Piero si mette in attesa. Sarà svegliato da Mario
|
||||
}
|
||||
else{
|
||||
V(mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process rosa(){
|
||||
while(true){
|
||||
waitTimer(); // Bloccata sulla waitTimer(). All'inizio timer=+infinito. Settato a 60 da Mario
|
||||
P(mutex); // Per la mutua esclusione con Piero
|
||||
V(mario); // Sveglia Mario
|
||||
P(rosa); // Si blocca in attesa che Mario frigga
|
||||
V(mutex);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
process vassoio{
|
||||
int b = 0;
|
||||
port String piero_rosa;
|
||||
String mess;
|
||||
port signal rilascio_vassoio;
|
||||
signal s;
|
||||
process pPieroRosa;
|
||||
process pMario;
|
||||
boolean attesaPiero = false;
|
||||
|
||||
send(b) to Mario.friggi;
|
||||
pMario = receive(s) from rilascio_vassoio;
|
||||
b = 30;
|
||||
while(true){
|
||||
pPieroRosa = receive(mess) from piero_rosa;
|
||||
if(mess=="piero"){
|
||||
b--;
|
||||
if(b==0){
|
||||
attesaPiero = true;
|
||||
send("finiti") to pPieroRosa.servi;
|
||||
setTimer(0);
|
||||
}
|
||||
else{
|
||||
send("non finiti") to pPieroRosa.servi;
|
||||
}
|
||||
}
|
||||
else if(mess=="rosa"){
|
||||
send(b) to Mario.friggi;
|
||||
pMario = receive(s) from rilascio_vassoio;
|
||||
b = 30;
|
||||
send(s) to Rosa.rilascio_rosa;
|
||||
if(attesaPiero) {
|
||||
attesaPiero = false;
|
||||
send(s) to Piero.riprendi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Mario{
|
||||
process p;
|
||||
signal s;
|
||||
port int friggi;
|
||||
int b_rim;
|
||||
while(true){
|
||||
p = receive(b_rim) from friggi;
|
||||
if(b_rim>0){
|
||||
<butta>
|
||||
}
|
||||
<friggi>
|
||||
setTimer(60);
|
||||
send(s) to p.rilascio_vassoio;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
process Piero{
|
||||
process pCliente;
|
||||
process pVassoio;
|
||||
port signal riprendi;
|
||||
signal s;
|
||||
port String servi;
|
||||
String mess;
|
||||
port signal cliente;
|
||||
|
||||
while(true){
|
||||
pCliente = receive(s) from cliente; // Aspetta un cliente
|
||||
send("piero") to vassoio.piero_rosa;
|
||||
pVassoio = receive(mess) from servi;
|
||||
<servo clienti>
|
||||
send(s) to pCliente.rilascio_cliente; // Comunica al cliente che lo ha servito
|
||||
if(mess == "finiti"){
|
||||
p = receive(s) from riprendi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Rosa{
|
||||
port signal rilascio_rosa;
|
||||
signal s;
|
||||
process p;
|
||||
while(true){
|
||||
waitTimer();
|
||||
send("rosa") to vassoio.piero_rosa;
|
||||
p = receive(s) from rilascio_rosa;
|
||||
}
|
||||
}
|
||||
|
||||
process Cliente{
|
||||
port signal rilascio_cliente;
|
||||
process p;
|
||||
signal s;
|
||||
|
||||
send(s) to Piero.cliente; // Chiede a Piero un bombolone
|
||||
p = receive(s) from rilascio_cliente; // Aspetta di riceverlo
|
||||
<Mangia bombolone> // Frigge il timer
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
monitor bomboloni{
|
||||
int b = 0;
|
||||
condition mario;
|
||||
condition piero;
|
||||
condition cliente;
|
||||
condition codaPiero;
|
||||
condition rosa;
|
||||
boolean deveFriggere = false;
|
||||
|
||||
public void mario(){
|
||||
if (!deveFriggere) wait(mario);
|
||||
if(b>0) <butta i bomboloni>
|
||||
<friggi bomboloni>
|
||||
deveFriggere = false;
|
||||
b=30;
|
||||
setTimer(60);
|
||||
signal(piero);
|
||||
if(!empty(rosa)){
|
||||
signal(rosa);
|
||||
}
|
||||
}
|
||||
|
||||
public void piero(){
|
||||
if (b == 0){
|
||||
setTimer(0);
|
||||
wait(piero);
|
||||
}
|
||||
if(empty(codaPiero)){ // Se non è ancora arrivato il cliente, Piero si mette in attesa..
|
||||
wait(cliente);
|
||||
b--;
|
||||
<Servi>
|
||||
}
|
||||
else{ // ..altrimenti lo serve e lo sveglia
|
||||
b--;
|
||||
<Servi>
|
||||
signal(codaPiero);
|
||||
}
|
||||
}
|
||||
|
||||
public void rosa(){
|
||||
deveFriggere = true;
|
||||
if(!empty(mario)){
|
||||
signal(mario);
|
||||
}
|
||||
else{
|
||||
wait(rosa);
|
||||
}
|
||||
}
|
||||
|
||||
public void cliente(){
|
||||
if(empty(cliente)) wait(codaPiero); // Se Piero non è ancora arrivato si mette in attesa..
|
||||
else signal(cliente) // ..altrimenti sveglia Piero
|
||||
}
|
||||
}
|
||||
|
||||
bomboloni alpha;
|
||||
process piero(){
|
||||
while(true){
|
||||
alpha.piero();
|
||||
}
|
||||
}
|
||||
|
||||
process mario(){
|
||||
while(true){
|
||||
alpha.mario();
|
||||
}
|
||||
}
|
||||
|
||||
process rosa(){
|
||||
while(true){
|
||||
waitTimer()
|
||||
alpha.rosa();
|
||||
}
|
||||
}
|
||||
|
||||
process cliente(){
|
||||
alpha.cliente();
|
||||
<Mangia>
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
process vassoio{
|
||||
int b = 0;
|
||||
entry piero_rosa(in String inMess, out String outMess);
|
||||
boolean attesaPiero = false;
|
||||
|
||||
call mario.friggi(b);
|
||||
b = 30;
|
||||
while(true){
|
||||
accept piero_rosa(in String inMess, out String outMess)
|
||||
{
|
||||
if(inMess=="piero"){
|
||||
b--;
|
||||
if(b==0){
|
||||
outMess = "finiti";
|
||||
attesaPiero = true;
|
||||
setTimer(0);
|
||||
}
|
||||
else{
|
||||
outMess = "non finiti";
|
||||
}
|
||||
}
|
||||
else if(inMess=="rosa"){
|
||||
call mario.friggi(b);
|
||||
b = 30;
|
||||
if (attesaPiero) {
|
||||
attesaPiero = false;
|
||||
call Piero.riprendi();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Mario{
|
||||
entry friggi(in int b_rimasti);
|
||||
|
||||
while(true){
|
||||
accept friggi(in int b_rim)
|
||||
{
|
||||
if(b_rim>0){
|
||||
<butta>
|
||||
}
|
||||
<friggi>
|
||||
setTimer(60);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Piero{
|
||||
entry cliente();
|
||||
entry riprendi();
|
||||
String mess;
|
||||
|
||||
while(true){
|
||||
accept cliente() // Aspetta un cliente
|
||||
{
|
||||
call vassoio.piero_rosa("piero");
|
||||
<servo clienti>
|
||||
}
|
||||
if (mess == "finiti") {
|
||||
accept riprendi()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Rosa{
|
||||
while(true){
|
||||
waitTimer();
|
||||
call vassoio.piero_rosa("rosa");
|
||||
}
|
||||
}
|
||||
|
||||
process Cliente{
|
||||
call Piero.cliente(); // Chiede a Piero un bombolone
|
||||
<Mangia bombolone>
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
semaphore mario = 0;
|
||||
semaphore cliente = 0;
|
||||
semaphore mutex = 1;
|
||||
semaphore rosa = 0;
|
||||
semaphore ricevi = 0;
|
||||
semaphore piero = 0;
|
||||
int b = 0;
|
||||
boolean attesaPiero = false;
|
||||
|
||||
process mario(){
|
||||
<friggi>
|
||||
b=30;
|
||||
setTimer(60);
|
||||
V(piero);
|
||||
while(true){
|
||||
P(mario);
|
||||
if(b>0) <Butta>
|
||||
<Friggi>
|
||||
b=30;
|
||||
setTimer(60);
|
||||
if(attesaPiero){
|
||||
attesaPiero = false;
|
||||
V(piero);
|
||||
}
|
||||
V(rosa);
|
||||
}
|
||||
}
|
||||
|
||||
process piero(){
|
||||
P(piero);
|
||||
while(true){
|
||||
P(cliente); // Aspetta la richiesta del cliente
|
||||
P(mutex);
|
||||
<Servi>
|
||||
V(ricevi); // Sblocca il cliente
|
||||
b--;
|
||||
if(b==0){
|
||||
attesaPiero = true;
|
||||
setTimer(0);
|
||||
V(mutex);
|
||||
P(piero);
|
||||
}
|
||||
else{
|
||||
V(mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process rosa(){
|
||||
while(true){
|
||||
waitTimer();
|
||||
P(mutex);
|
||||
V(mario);
|
||||
P(rosa);
|
||||
V(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
process cliente(){
|
||||
V(cliente); // Richiede bombolone svegliando Piero
|
||||
P(ricevi); // Aspetta di ricevere il bombolone da Piero
|
||||
<Mangia bombolone>
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
Process Bancone {
|
||||
int nbomb = 0;
|
||||
long tick = 0;
|
||||
long alarm = -1;
|
||||
boolean butta = false;
|
||||
signal s;
|
||||
port signal friggi_i;
|
||||
port signal friggi_f;
|
||||
port signal servi_i;
|
||||
port signal servi_f;
|
||||
port signal wait_timer;
|
||||
port long set_timer;
|
||||
port signal scaduti;
|
||||
|
||||
do
|
||||
[] nbomb == 0 or butta; p = receive(s) from friggi_i ->
|
||||
send(nbomb) to p.ok;
|
||||
|
||||
[] ; receive(s) from friggi_f;
|
||||
nbomb = 30;
|
||||
butta = false;
|
||||
alarm = tick + 60;
|
||||
|
||||
[] nbomb > 0 and !butta; p = receive(s) from servi_i ->
|
||||
send(s) to p.ok;
|
||||
|
||||
[] ; receive(s) from servi_f ->
|
||||
nbomb--;
|
||||
|
||||
[] tick >= alarm; p = receive(s) from wait_timer ->
|
||||
send(s) to p.ok;
|
||||
|
||||
[] butta == false; receive(s) from scaduti ->
|
||||
butta = true;
|
||||
|
||||
[] ; receive(s) from clock ->
|
||||
tick++;
|
||||
|
||||
[] ; receive(n) from set_timer ->
|
||||
alarm += n;
|
||||
|
||||
od
|
||||
}
|
||||
|
||||
Process Mario {
|
||||
int b_rimasti = 0;
|
||||
signal s;
|
||||
port int ok;
|
||||
|
||||
while(true){
|
||||
send(s) to Bancone.friggi_i;
|
||||
receive(b_rimasti) from ok;
|
||||
if(b_rimasti) < butta >;
|
||||
< friggi bomboloni >
|
||||
send(s) to Bancone.friggi_f;
|
||||
}
|
||||
}
|
||||
|
||||
Process Piero {
|
||||
signal s;
|
||||
port signal ok;
|
||||
|
||||
while(true){
|
||||
send(s) to Bancone.servi_i;
|
||||
receive(s) from ok;
|
||||
< servi cliente >
|
||||
send(s) to Bancone.servi_f;
|
||||
}
|
||||
}
|
||||
|
||||
Process Rosa {
|
||||
signal s;
|
||||
port signal ok;
|
||||
|
||||
while(true){
|
||||
send(s) to Bancone.wait_timer;
|
||||
receive(s) from ok;
|
||||
send(s) to Bancone.scaduti;
|
||||
send(s) to Bancone.settimer(60);
|
||||
}
|
||||
}
|
||||
|
||||
Process Clock {
|
||||
signal s;
|
||||
while(true){
|
||||
< hw clock >
|
||||
send(s) to Bancone.clock;
|
||||
}
|
||||
}
|
90
anno2/YearI/MCAD/esercizi2019/bomboloni_sol/mia_monitor
Normal file
|
@ -0,0 +1,90 @@
|
|||
Monitor Bancone{
|
||||
condition friggi;
|
||||
condition pronti;
|
||||
int nbomb = 0;
|
||||
boolean butta = false;
|
||||
|
||||
public int friggi_i(){
|
||||
if(nbomb > 0 and !butta)
|
||||
wait(friggi);
|
||||
return nbomb;
|
||||
|
||||
public void friggi_f(){
|
||||
nbomb = 30;
|
||||
butta = false;
|
||||
}
|
||||
|
||||
public void servi_i(){
|
||||
if(nbomb == 0)
|
||||
wait(pronti);
|
||||
}
|
||||
|
||||
public void servi_f(){
|
||||
nbomb--;
|
||||
if(nbomb == 0)
|
||||
signal(friggi);
|
||||
}
|
||||
|
||||
public void scaduti(){
|
||||
butta = true;
|
||||
signal(friggi);
|
||||
}
|
||||
}
|
||||
|
||||
Monitor Clock{
|
||||
long tick = 0;
|
||||
long alarm = -1;
|
||||
condition sleeping;
|
||||
|
||||
public void wait_timer(){
|
||||
if(alarm >= tick and alarm != -1)
|
||||
wait(sleeping)
|
||||
}
|
||||
|
||||
public void tick(){
|
||||
tick++;
|
||||
if(tick >= alarm and !empty sleeping)
|
||||
signal(sleeping);
|
||||
}
|
||||
|
||||
public void set_timer(long n){
|
||||
alarm = tick + n;
|
||||
}
|
||||
}
|
||||
|
||||
Clock clock;
|
||||
Bancone bancone;
|
||||
|
||||
Process Mario{
|
||||
while(true){
|
||||
int rimasti = bancone.friggi_i();
|
||||
if(rimasti > 0) < butta >
|
||||
< friggi i bomboloni >
|
||||
bancone.friggi_f();
|
||||
clock.set_timer(1 ora);
|
||||
}
|
||||
}
|
||||
|
||||
Process Piero{
|
||||
while(true){
|
||||
< attendi cliente >
|
||||
bancone.servi_i();
|
||||
< servi cliente >
|
||||
bancone.servi_f();
|
||||
}
|
||||
}
|
||||
|
||||
Process Rosa{
|
||||
while(true){
|
||||
clock.wait_timer();
|
||||
bancone.butta();
|
||||
clock.set_timer(1 ora);
|
||||
}
|
||||
}
|
||||
|
||||
Process ClockRuns{
|
||||
while(true){
|
||||
< hardware tick >
|
||||
clock.tick();
|
||||
}
|
||||
}
|
76
anno2/YearI/MCAD/esercizi2019/bomboloni_sol/mia_rendevouz
Normal file
|
@ -0,0 +1,76 @@
|
|||
Process Bancone {
|
||||
int nbomb = 0;
|
||||
long tick = 0, alarm = -1;
|
||||
bool butta = false;
|
||||
entry friggi_i(out int rimasti);
|
||||
entry friggi_f;
|
||||
entry servi_cliente_i;
|
||||
entry servi_cliente_f;
|
||||
entry scaduti;
|
||||
entry clock;
|
||||
entry wait_timer;
|
||||
entry set_timer (in long n);
|
||||
|
||||
do
|
||||
[] nbomb == 0 or butta; accept friggi_i(out nrimasti);
|
||||
nrimasti = nbomb;
|
||||
|
||||
[] ; accept friggi_f() ->
|
||||
nbomb = 30;
|
||||
alarm = tick + minuti(60);
|
||||
butta = false;
|
||||
|
||||
[] nbomb > 0 and !butta; accept servi_cliente_i();
|
||||
|
||||
[] ; accept servi_cliente_f() ->
|
||||
nbomb--;
|
||||
|
||||
[] ; accept clock(); ->
|
||||
tick++;
|
||||
|
||||
[] tick >= alarm and alarm != -1; accept wait_timer() ->
|
||||
butta = true;
|
||||
nbomb = 0;
|
||||
alarm = -1;
|
||||
|
||||
[] ; accept set_timer(in long n) ->
|
||||
alarm = tick + n;
|
||||
|
||||
[] ; accept scaduti ->
|
||||
butta = true;
|
||||
od;
|
||||
}
|
||||
|
||||
Process Mario {
|
||||
int nrimasti;
|
||||
while(true){
|
||||
call Bancone.friggi_i(nrimasti);
|
||||
if(nrimasti) < butta >;
|
||||
< friggi bomboloni >
|
||||
call Bancone.set_timer(60);
|
||||
call Bancone.friggi_f();
|
||||
}
|
||||
}
|
||||
|
||||
Process Piero {
|
||||
while(true){
|
||||
< attendi cliente >
|
||||
call Bancone.servi_cliente_i();
|
||||
< servi cliente >
|
||||
call Bancone.servi_cliente_f();
|
||||
}
|
||||
}
|
||||
|
||||
Process Rosa {
|
||||
while(true){
|
||||
call Bancone.wait_timer();
|
||||
call Bancone.butta();
|
||||
}
|
||||
}
|
||||
|
||||
Process Clock {
|
||||
while(true){
|
||||
< hardware clock ticks >
|
||||
call Bancone.clock();
|
||||
}
|
||||
}
|
53
anno2/YearI/MCAD/esercizi2019/bomboloni_sol/mia_semafori
Normal file
|
@ -0,0 +1,53 @@
|
|||
Semaphore mut = 1;
|
||||
Semaphore pierasem = 0;
|
||||
Semaphore mariosem = 1;
|
||||
Semaphore pierosem = 0
|
||||
int nbomb = 0;
|
||||
boolean attesaPiero;
|
||||
|
||||
Process Mario{
|
||||
while(true){
|
||||
P(mariosem);
|
||||
< friggi >
|
||||
P(mut);
|
||||
nbomb = 30;
|
||||
if(attesaPiero)
|
||||
attesaPiera = false;
|
||||
V(pierosem);
|
||||
if(butta == true) // e` stata Rosa
|
||||
butta = false;
|
||||
V(rosasem)
|
||||
V(mut);
|
||||
}
|
||||
}
|
||||
|
||||
Process Piero{
|
||||
while(true){
|
||||
< attesa cliente >
|
||||
P(pierosem);
|
||||
P(mut);
|
||||
nbomb --;
|
||||
V(mut)
|
||||
|
||||
< servi cliente >
|
||||
|
||||
P(mut)
|
||||
if(nbomb == 0)
|
||||
V(mariosem);
|
||||
attesaPiero = true;
|
||||
else
|
||||
V(pierosem);
|
||||
V(mut)
|
||||
}
|
||||
}
|
||||
|
||||
Process Rosa{
|
||||
while(true){
|
||||
wait_timer();
|
||||
P(mut);
|
||||
butta = true;
|
||||
V(mut);
|
||||
P(rosasem);
|
||||
set_timer(60);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
int nbomb = 0;
|
||||
bool butta = false;
|
||||
bool attesa_piero = true;
|
||||
Semaphore piero_s = 0;
|
||||
Semaphore mario_s = 1;
|
||||
Semaphore rosa_s = 0;
|
||||
Semaphore cliente_s = 0;
|
||||
Semaphore ricevi_s = 0;
|
||||
Semaphore mutex = 1;
|
||||
|
||||
Process Mario{
|
||||
while(1){
|
||||
V(mario_s);
|
||||
< friggi >
|
||||
P(mutex);
|
||||
nbomb = 30;
|
||||
set_timer(60 minuti);
|
||||
if(attesa_piero == true){
|
||||
V(piero_s);
|
||||
}
|
||||
if(da_buttare == true){
|
||||
da_buttare = false;
|
||||
V(rosa_s);
|
||||
}
|
||||
V(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
Process Piero{
|
||||
while(1){
|
||||
P(cliente_s);
|
||||
P(mutex);
|
||||
if(butta == false AND attesa_piero == false){
|
||||
V(piero_s);
|
||||
}
|
||||
V(mutex);
|
||||
P(piero_s);
|
||||
< servi cliente >
|
||||
V(ricevi_s);
|
||||
P(mutex);
|
||||
nbomb--;
|
||||
if(nbomb == 0 and butta == false){
|
||||
attesa_piero = true;
|
||||
V(mario_s);
|
||||
}
|
||||
V(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
Process Rosa{
|
||||
set_timer(60 minuti);
|
||||
while(1){
|
||||
wait_timer();
|
||||
P(mutex);
|
||||
butta = true;
|
||||
V(mutex);
|
||||
P(rosa_s);
|
||||
}
|
||||
}
|
||||
|
||||
Process Cliente{
|
||||
V(cliente_s);
|
||||
P(ricevi_s);
|
||||
< consuma >;
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
process vassoio{
|
||||
int b = 0;
|
||||
port String piero_rosa;
|
||||
String mess;
|
||||
port signal rilascio_vassoio;
|
||||
signal s;
|
||||
process p;
|
||||
boolean attesaPiero = false;
|
||||
|
||||
send(b) to mario.friggi;
|
||||
p = receive(s) from rilascio_vassoio;
|
||||
b = 30;
|
||||
while(true){
|
||||
p = receive(mess) from piero_rosa;
|
||||
if(mess=="piero"){
|
||||
b--;
|
||||
if(b==0){
|
||||
attesaPiero = true;
|
||||
send("finiti") to p.servi;
|
||||
send(0) to clock.setTimer;
|
||||
}
|
||||
else{
|
||||
send("non finiti") to p.servi;
|
||||
}
|
||||
}
|
||||
else if(mess=="rosa"){
|
||||
send(b) to mario.friggi;
|
||||
p = receive(s) from rilascio_vassoio;
|
||||
b = 30;
|
||||
send(s) to rosa.rilascio_rosa;
|
||||
if(attesaPiero){
|
||||
attesaPiero = false;
|
||||
send(s) to Piero.riprendi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Mario{
|
||||
process p;
|
||||
signal s;
|
||||
port int friggi;
|
||||
int b_rim;
|
||||
while(true){
|
||||
p = receive(b_rim) from friggi;
|
||||
if(b_rim>0)
|
||||
<butta>
|
||||
<friggi>
|
||||
send(60) to clock.setTimer;
|
||||
send(s) to p.rilascio_vassoio;
|
||||
}
|
||||
}
|
||||
|
||||
process Piero{
|
||||
process p;
|
||||
port signal riprendi;
|
||||
signal s;
|
||||
port String servi;
|
||||
String mess;
|
||||
while(true){
|
||||
<attendi clienti>
|
||||
send("piero") to vassoio.piero_rosa;
|
||||
p = receive(mess) from servi;
|
||||
<servo cliente>
|
||||
if(mess == "finiti"){
|
||||
p = receive(s) from riprendi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Rosa{
|
||||
port signal rilascio_rosa;
|
||||
signal s;
|
||||
process p;
|
||||
while(true){
|
||||
send(s) to clock.waitTimer;
|
||||
p = receive(s) from rilascio_rosa;
|
||||
send("rosa") to vassoio.piero_rosa;
|
||||
p = receive(s) from rilascio_rosa;
|
||||
}
|
||||
}
|
||||
|
||||
process Clock{
|
||||
port Signal waitTimer;
|
||||
port int setTimer;
|
||||
port Signal tick;
|
||||
signal s;
|
||||
process p;
|
||||
int min;
|
||||
int tempo = 0;
|
||||
int sveglia = 0;
|
||||
boolean inAscolto = false;
|
||||
do
|
||||
[] p = receive(s) from tick; -> // Aumenta il tempo e se il timer scade..
|
||||
tempo++;
|
||||
if(sveglia == tempo && inAscolto){ // ..se il tempo è uguale alla sveglia e Rosa è sulla receive...
|
||||
inAscolto = false;
|
||||
send(s) to rosa.rilascio_rosa; // ..allora sveglia Rosa
|
||||
sveglia = 0;
|
||||
}
|
||||
[] p = receive(min) from setTimer; -> // Viene settato il Timer a zero o a 60
|
||||
sveglia = tempo + min;
|
||||
if(sveglia == tempo && inAscolto){ // Se il tempo è uguale alla sveglia e Rosa è sulla receive...
|
||||
inAscolto = false;
|
||||
send(s) to rose.rilascio_rosa; // ..llora sveglia Rosa
|
||||
sveglia = 0;
|
||||
}
|
||||
|
||||
[] p = receive(s) from waitTimer; -> // Si mette Rosa sulla receive accettando la send di waitTimer
|
||||
inAscolto = true;
|
||||
od
|
||||
}
|
||||
process Tempo{
|
||||
signal s;
|
||||
while(true){
|
||||
<aspetta un secondo>
|
||||
send(s) to clock.tick;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
monitor Clock{
|
||||
condition attesa;
|
||||
int tempo = <tempo di inizializzazione>;
|
||||
int sveglia = 0;
|
||||
|
||||
public void tick(){ // Funzione che incrementa il tempo e controlla se deve svegliare Rosa
|
||||
time++;
|
||||
if(tempo==sveglia){
|
||||
signal(attesa); // Sveglia Rosa dalla waitTimer()
|
||||
}
|
||||
}
|
||||
|
||||
public void waitTimer(){
|
||||
wait(attesa);
|
||||
}
|
||||
|
||||
public void setTimer(int min){
|
||||
if(min == 0){ // Se Piero ha fatto una setTimer(0)..
|
||||
signal(attesa); // ..allora sveglio Rosa dalla waitTimer()
|
||||
}
|
||||
else{
|
||||
sveglia = tempo + min; // Altrimenti setto normalmente la sveglia
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
monitor bomboloni{
|
||||
int b = 0;
|
||||
condition mario;
|
||||
condition piero;
|
||||
condition cliente;
|
||||
condition rosa;
|
||||
boolean deveFriggere = false;
|
||||
|
||||
public void mario(){
|
||||
if (!deveFriggere) wait(mario);
|
||||
if(b>0) <butta i bomboloni>
|
||||
<friggi bomboloni>
|
||||
deveFriggere = false;
|
||||
b=30;
|
||||
clock.setTimer(60);
|
||||
signal(piero);
|
||||
if(!empty(rosa))signal(rosa);
|
||||
}
|
||||
|
||||
public void piero(){
|
||||
if (b == 0){
|
||||
clock.setTimer(0);
|
||||
wait(piero);
|
||||
}
|
||||
wait(cliente);
|
||||
b--;
|
||||
}
|
||||
|
||||
public void rosa(){
|
||||
deveFriggere = true;
|
||||
if(!empty(mario)){
|
||||
signal(mario);
|
||||
}
|
||||
else{
|
||||
wait(rosa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bomboloni alpha;
|
||||
Clock clock;
|
||||
|
||||
process piero(){
|
||||
while(true){
|
||||
alpha.piero();
|
||||
<Servi>
|
||||
}
|
||||
}
|
||||
|
||||
process mario(){
|
||||
while(true){
|
||||
alpha.mario();
|
||||
}
|
||||
}
|
||||
|
||||
process rosa(){
|
||||
while(true){
|
||||
clock.waitTimer()
|
||||
alpha.rosa();
|
||||
}
|
||||
}
|
||||
|
||||
process Tempo{
|
||||
while(true){
|
||||
<"aspetta un secondcondo">
|
||||
clock.tick();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
process vassoio{
|
||||
int b = 0;
|
||||
boolean attesaPiero = false;
|
||||
entry piero_rosa(in String inMess, out String outMess);
|
||||
|
||||
call mario.friggi(b);
|
||||
b = 30;
|
||||
while(true){
|
||||
accept piero_rosa(in String inMess, out String outMess)
|
||||
{
|
||||
if(inMess=="piero"){
|
||||
b--;
|
||||
if(b==0){
|
||||
outMess = "finiti";
|
||||
attesaPiero = true;
|
||||
call clock.setTimer(0);
|
||||
}
|
||||
else{
|
||||
outMess = "non finiti";
|
||||
}
|
||||
}
|
||||
else if(inMess=="rosa"){
|
||||
call mario.friggi(b);
|
||||
b = 30;
|
||||
if (attesaPiero) {
|
||||
attesaPiero = false;
|
||||
call Piero.riprendi();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Mario{
|
||||
entry friggi(in int b_rimasti);
|
||||
while(true){
|
||||
accept friggi(in int b_rim)
|
||||
{
|
||||
if(b_rim>0)
|
||||
<butta>
|
||||
<friggi>
|
||||
call clock.setTimer(60);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Piero{
|
||||
entry riprendi();
|
||||
String mess;
|
||||
|
||||
while(true){
|
||||
<attendi clienti>
|
||||
call vassoio.piero_rosa("piero");
|
||||
<servo cliente>
|
||||
if (mess == "finiti") {
|
||||
accept riprendi()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process Rosa{
|
||||
while(true){
|
||||
call clock.waitTimer();
|
||||
call vassoio.piero_rosa("rosa");
|
||||
}
|
||||
}
|
||||
|
||||
process Clock{
|
||||
entry waitTimer();
|
||||
entry setTimer(in int min);
|
||||
entry tick;
|
||||
int tempo = 0;
|
||||
int sveglia = 0;
|
||||
do
|
||||
[] accept tick -> tempo++; // Incrementa il tempo
|
||||
[] accept setTimer(in int min)
|
||||
{
|
||||
if(min == 0){ // Se Piero ha fatto una setTimer(0)..
|
||||
accept waitTimer() // ..allora sveglio Rosa dalla waitTimer()
|
||||
}
|
||||
else{
|
||||
sveglia = tempo + min; // Altrimenti setto normalmente la sveglia
|
||||
}
|
||||
}
|
||||
[](tempo == sveglia); accept waitTimer() // Se è il momento di svegliare Rosa accettiamo la sua call
|
||||
od
|
||||
}
|
||||
|
||||
|
||||
process Tempo{
|
||||
while(true){
|
||||
<aspetta un secondo>
|
||||
call clock.tick;
|
||||
}
|
||||
}
|
BIN
anno2/YearI/MCAD/esercizi2019/esercitazione.19.doc
Normal file
BIN
anno2/YearI/MCAD/esercizi2019/esercizimonitor19.pptx
Normal file
BIN
anno2/YearI/MCAD/esercizi2019/invarianti19.docx
Normal file
BIN
anno2/YearI/MCAD/esercizi2019/invarianti19.pdf
Normal file
BIN
anno2/YearI/MCAD/foto_luca/es2_invarianti.2.jpg
Normal file
After Width: | Height: | Size: 112 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es2_p1.jpg
Normal file
After Width: | Height: | Size: 135 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es3_p1.jpg
Normal file
After Width: | Height: | Size: 134 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es3_p2.jpg
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es4B_dani1jpg
Normal file
After Width: | Height: | Size: 106 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es4B_dani2,jpg
Normal file
After Width: | Height: | Size: 103 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es4_2_luca.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es4_dani1.jpg
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es4_dani2.jpg
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es4_dani3.jpg
Normal file
After Width: | Height: | Size: 125 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es4_luca.jpg
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es5.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
anno2/YearI/MCAD/foto_luca/es6.jpg
Normal file
After Width: | Height: | Size: 131 KiB |
BIN
anno2/YearI/MCAD/foto_luca/ex1.jpg
Normal file
After Width: | Height: | Size: 112 KiB |
BIN
anno2/YearI/MCAD/foto_luca/ex1_d.jpg
Normal file
After Width: | Height: | Size: 107 KiB |
|
@ -0,0 +1,52 @@
|
|||
int maxA,maxR;
|
||||
int A=0,R=1;
|
||||
|
||||
process contenitore {
|
||||
port int inizio_vers[2];
|
||||
port signal fine_vers;
|
||||
port signal inizio_estr;
|
||||
port signal fine_estr;
|
||||
process v, e;
|
||||
signal s;
|
||||
int qA,qR;
|
||||
int va = 0;
|
||||
do
|
||||
qA+1<maxA; v=receive from inizio_vers[A] ->
|
||||
qA++;
|
||||
va++;
|
||||
send(s) to v.ok;
|
||||
qR+1<maxR; v=receive from inizio_vers[R] ->
|
||||
qR++;
|
||||
va++;
|
||||
send(s) to v.ok;
|
||||
receive(s) from fine_vers ->
|
||||
va --;
|
||||
va==0 && qA==2*qR && qA+qR>=C; e=receive(s) from inizio_estr ->
|
||||
send(s) to e.ok;
|
||||
receive(s) from fine_estr
|
||||
qA = qA - 2/3*C;
|
||||
qR = qR - 1/3*C;
|
||||
od
|
||||
}
|
||||
|
||||
process versatore(X) {
|
||||
signal s;
|
||||
port ok;
|
||||
while(true){
|
||||
send(X) to contenitore.inizio_vers[X];
|
||||
receive(s) from ok;
|
||||
<versa>
|
||||
send(s) to contenitore.fine_vers;
|
||||
}
|
||||
}
|
||||
|
||||
process estrattore() {
|
||||
signal s;
|
||||
port ok;
|
||||
while(true) {
|
||||
send(s) to contenitore.inizio_estr;
|
||||
receive(s) from ok;
|
||||
<estrai>
|
||||
send(s) to contenitore.fine_estr;
|
||||
}
|
||||
}
|
BIN
anno2/YearI/MCAD/gdrive/Lynch. Distributed Algorithms.pdf
Normal file
55
anno2/YearI/MCAD/lesson1-03102017.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Introduzione - Programmazione Concorrente
|
||||
|
||||
## Concorrenza
|
||||
|
||||
La concorrenza di un programma e' l'interleaving di istruzioni atomiche.
|
||||
In questo corso l'ipotesi generale e' che **l'operazione (statement) di assegnazione e' atomico**.
|
||||
|
||||
## Atomicita'
|
||||
|
||||
Differenti architetture supportano differenti **granularita'** di atomicita'.
|
||||
La granularita' e' la piu' piccola istruzione atomica eseguibile dall'architettura.
|
||||
|
||||
* Monoprocessore -> istruzione macchina
|
||||
* Multiprocessore (memoria **comune**) -> accesso alla memoria comune (microistruzioni LOAD/STORE)
|
||||
|
||||
-> Per valutare la **correttezza** di un programma concorrente e' necessario conoscere alcuni dettagli architetturali.
|
||||
|
||||
## Correttezza di un programma concorrente
|
||||
|
||||
La correttezza di un programma concorrente e' l'assenza di problematiche legate alla concorrenza/accesso alle risorse.
|
||||
Pertanto, un programma concorrente e' corretto se non produce **deadlock, starvation, race conditions, etc**.
|
||||
|
||||
### Proprieta'
|
||||
|
||||
Gli statements di Lamport definiscono le due proprieta' di correttezza di un programma concorrente:
|
||||
|
||||
* **Safety**: la proprieta P deve **sempre essere vera**, in ogni istante della computazione.
|
||||
* **Liveness**: la proprieta P **prima o poi deve essere vera**, in un determinato istante della computazione.
|
||||
|
||||
Legata alla proprieta' di liveness c'e' la Proprieta di **Fairness**:
|
||||
Una computazione che per ogni suo stato ha uno *statement abilitato* che prima o poi sara' eseguito. Questo vuol dire che ogni statement viene eseguito (e quindi non ci sono statement che subiscono starvation). Richiede conoscenze hardware (Arbitro di Memoria, architettura processore, scheduler per single core, etc)
|
||||
|
||||
## Linguaggi Concorrenti
|
||||
|
||||
Deve implementare *moduli sequenziali asincroni interagenti*. (vedi slides, banale)
|
||||
|
||||
## Macchina Concorrente
|
||||
|
||||
Macchina virtuale o astratta, spesso basata su una macchina fisica molto piu' semplice.
|
||||
|
||||
* il Kernel e' la parte SW che si identifica come supporto run time, fornendo multiprogrammazione e sincronizzazione di moduli.
|
||||
|
||||
Due configurazioni di memoria:
|
||||
* Comune, unica per le unita' di elaborazione
|
||||
* Modello a Rete, elaboratori collegati da una sottorete di comunicazione, non condividono memoria.
|
||||
|
||||
Nota: modello a rete non implica piu' processori, puo' essere costruito su qualunque architettura. Un monoprocessore puo' effettivamente costituire un modello a reti nel caso la memoria sia separata per ogni processo.
|
||||
|
||||
(vedi slides per tabella di architettura)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
143
anno2/YearI/MCAD/lesson11-08112017.md
Normal file
|
@ -0,0 +1,143 @@
|
|||
# Esercizi
|
||||
|
||||
## 5 filosofi
|
||||
|
||||
*(slides per implementazione monitor)*
|
||||
### Dimostrazione di correttezza
|
||||
|
||||
E' necessario dimostrare la correttezza della seguente:
|
||||
```
|
||||
SUM(i from 0 to 4) fork[i] + 2*E = 10
|
||||
```
|
||||
Dove E e' il **numero di filosofi che mangiano** (hanno fatto *takeforks* ma non *release*).
|
||||
|
||||
* Analizzando *takeforks*, quando un processo esce, **aumenta E di 1**. Di fatto pero', diminuisco due *forks* e aumento di 1 E, di modo che l'equaglianza in tesi rimanga valida.
|
||||
|
||||
* Analizzando *release*, questa si comporta in maniera simmetrica a *takeforks*: aumenta di 2 forks e diminuisce di 1 E, preservando l'eguaglianza in tesi. La funzione quindi **sveglia un altro processo**, che riprende da *takeforks*. Quando questo termina, viene eseguito un altro *signal* che sveglia un secondo processo.
|
||||
|
||||
L'eguaglianza e' sempre valida, essendo che non ci sono condizioni in cui le funzioni creino uno stato invalido.
|
||||
|
||||
## Molecole d'acqua (monitor)
|
||||
|
||||
Il problema e' stato trattato in precedenza con l'utilizzo dei semafori, e' necessario dimostrare la correttezza della soluzione monitor.
|
||||
|
||||
*(vedi slides per implementazione)*
|
||||
|
||||
L'invariante in tesi e':
|
||||
```
|
||||
0 <= nH - 2*nO <= 2
|
||||
```
|
||||
Che deve quindi essere sempre vera.
|
||||
|
||||
* Definiamo `count(t) = nH(t) - 2*nO(t)` dove `t` e' un qualunque istante durante l'esecuzione del processo.
|
||||
|
||||
* All'inizio, `count(0) = 0`
|
||||
|
||||
* Dopo una chiamata di *stampaH*, ho incrementato di 1 `nH` e `count`, che quindi rimane valido.
|
||||
|
||||
* Dopo una chiamata di *stampaO*, pongo `count` a 0 se `count == 2` e' valida. Questo significa:
|
||||
```
|
||||
nH(t) - 2*nO(t) = 2
|
||||
nH(t) = 2*nO(t) + 2
|
||||
```
|
||||
* Pertanto, porre `count = 0` e' corretto, in quanto sto considerando `nO = 0`. Il valore massimo di `count` sara' quindi 2, dato che ogni volta che raggiunge 2 viene resettato.
|
||||
|
||||
L'eguaglianza e' quindi dimostrata, in quanto non ci sono condizioni in cui `count` sia invalido.
|
||||
|
||||
# Programmazione Concorrente - Modello a Scambio di Messaggi
|
||||
|
||||
I processori (fisici o virtuali) hanno una propria **memoria privata** e quindi utilizzano una **rete di comunicazione** per interagire (LAN, WAN, architettura monoprocessore con multiplexing CPU (e partizioni di memoria **fisse**)).
|
||||
|
||||
## Caratteristiche del sistema
|
||||
|
||||
* Ogni processo puo' accedere **unicamente alle risorse allocate nella propria risorsa**.
|
||||
* Ogni risorsa del sistema **e' accessibile ad un solo processo (alla volta)**.
|
||||
* Se una risorsa e' necessaria a piu' processi, bisognera' definire un **processo server** come gestore della risorsa, che esegue le operazioni richieste dagli altri processi e ritorna i risultati.
|
||||
|
||||
Questo modello **client-server** da luogo a una programmazione concorrente definita **centralizzata**, opposta alla programmazione concorrente **distribuita**.
|
||||
|
||||
## Modello a scambio di messaggi
|
||||
|
||||
### Aspetti caratterizzanti
|
||||
|
||||
Il concetto fondamentale e' quello di **canale**, ossia collegamento **logico** tra due processi (**pipe**). Il **nucleo** del sistema (kernel, sistema di rete...) deve quindi fornire delle primitive di canali come mezzo di comunicazione tra processi. Il linguaggio di programmazione deve quindi fornire strutture ad alto livello per la gestione delle primitive.
|
||||
|
||||
#### Parametri di un canale
|
||||
|
||||
* Tipologia del canale (direzione flusso dati)
|
||||
|
||||
* Sorgente e destinatario della comunicazione, nonche' la **designazione** del canale. A seconda del tipo di comunicazione (asimmetrica / simmetrica) puo' essere necessario conoscere solo il destinatario oppure entrambi.
|
||||
|
||||
* Tipo di sincronizzazione adottata tra i processi.
|
||||
|
||||
#### Tipi di canale
|
||||
|
||||
* **link**: canale simmetrico (uno - uno).
|
||||
|
||||
* **port**: canale asimmetrico (molti - uno). Questa e' utilizzata per la struttura **client-server**. Qui, una *port* e' di proprieta' di un processo **server**, che e' noto a vari processi **client**. Il server riceve i messaggi sulla port e li gestisce, fornendo il servizio richiesto. Molto usata a livello kernel (gestione di code, etc)
|
||||
|
||||
* **mailbox**: canale asimmetrico (molti - molti)
|
||||
|
||||
*(vedi slides per rappresentazione grafica)*
|
||||
|
||||
#### Tipi di sincronizzazione
|
||||
|
||||
* Comunicazione **asincrona**
|
||||
* Comunicazione **sincrona** (**rendez-vous tra processi**)
|
||||
* Comunicazione con **sincronizzazione estesa** (come precedente, ma forza la risposta del servizio richiesto). Questa e' la struttura delle cosiddette **procedure estese**.
|
||||
|
||||
### Primitive di Comunicazione
|
||||
|
||||
* Dichiarazione di canale *asimmetrico molti - uno*:
|
||||
```
|
||||
port <tipo> <id> // ex: port int channel1;
|
||||
```
|
||||
* Invio:
|
||||
```
|
||||
send (<var>) to <port>;
|
||||
```
|
||||
* Ricezione:
|
||||
```
|
||||
receive(<var>) from <port>;
|
||||
```
|
||||
Nota: port identifica il canale, mentre **var e' una variabile dello stesso tipo di port**, che e' identificatore di una variabile a cui assegnare il processo.
|
||||
|
||||
* Comandi con **guardia**:
|
||||
```
|
||||
(<boolean expression>); <primitiva receive>
|
||||
```
|
||||
La guardia e' una **tuple** di espressione booleana / primitiva receive che viene valutata e puo' fornire diversi valori.
|
||||
|
||||
#### Guardia: valori e funzionamento
|
||||
|
||||
1. **guardia fallita**: l'espressione booleana e' *false*
|
||||
2. **guardia ritardata**: l'espressione booleana e' *vera*, ma bloccata (receive e' bloccante)
|
||||
3. **guardia valida**: l'espressione booleana e' *vera* e la receive puo' essere eseguita
|
||||
|
||||
L'esecuzione di una guardia e' strutturata con una struttura if/else in cui vengono valutate le guardie di tutti i rami, ese ci sono **piu' guardie valide**, ne viene eseguita una **scelta in maniera non deterministica**.
|
||||
Se **tutte le guardie valide sono ritardate**, il processo si sospende **in attesa che una delle guardie sia abilitata da un nuovo messaggio**.
|
||||
Se **non ci sono guardie valide / ritardate**, il proceso viene terminato (ma non viene considerato un errore, quindi puo' essere utilizzato similmente al *break*).
|
||||
|
||||
*(vedi slides per esempi)*
|
||||
|
||||
### Primitive Asincrone
|
||||
|
||||
Comunicazione asimmetrica:
|
||||
* **send non bloccante**
|
||||
* **receive bloccante**
|
||||
|
||||
I canali sono definiti mediante il costrutto *port*.
|
||||
|
||||
*(vedi slides per esempi)*
|
||||
*(vedi slides per comparison monitor / messaggi client-server)*
|
||||
|
||||
#### Simulazione di un semaforo
|
||||
|
||||
E' piu' potente la primitiva semaforica oppure un sistema a scambio di messaggi asimmetrico client / server?
|
||||
|
||||
Il passaggio da semafori a scambio di messaggi e' una variante dello schema producer / consumer. Il contrario (scambio di messaggi -> semaforo), *illustrato sulle slides*, implica la **creazione di un processo utilizzando le primitive di scambio di messaggi.**
|
||||
Il processo `process semaphore` sara' quindi l'implementazione contenente due *port*: `port signal P` e `port signal V`, che saranno le uniche operazioni eseguibili sul semaforo.
|
||||
L'**atomicita'** di P e V, richiesta dalla definizione classica di semaforo, e' da discutere.
|
||||
|
||||
* V e' sicuramente atomica, perche' **il semaforo e' l'unico ad avere accesso alle sue risorse** (non interrompibile).
|
||||
* P e' chiamata da un processo che effettua una richiesta, ed e' costituito da una decrementazione di `valore` e da una send. Il `valore`, come per V, e' unico al semaforo, e send non e' interrompibile, pertanto l'implementazione *sulle slides* e' valida.
|
108
anno2/YearI/MCAD/lesson12-14112017.md
Normal file
|
@ -0,0 +1,108 @@
|
|||
# Primitive di comunicazione asincrone
|
||||
|
||||
Comunicazione asimmetrica:
|
||||
* send non bloccante
|
||||
* receive non bloccante
|
||||
|
||||
I canali sono definiti mediante un costrutto `port`.
|
||||
*(vedi lesson 11)*
|
||||
|
||||
E' la primitiva di piu' basso livello, analogamente ai semafori, soprattutto se il kernel non e' in grado di fornire primitive a memoria condivisa.
|
||||
|
||||
## Scambio di dati e modello client/server
|
||||
|
||||
### Producer and Consumer
|
||||
|
||||
Data una `port dati`:
|
||||
|
||||
* Il produttore esegue `send() to dati`
|
||||
* il consumatore esegue `p=receive() from dati`
|
||||
|
||||
A differenza di produttore e consumatore nel caso di semafori e monitor, **non c'e' controllo sulla dimensione del canale**. Scambio di dati con buffer **potenzialmente infinito**, che pero' potrebbe essere riempito da applicazioni errate.
|
||||
|
||||
### Singolo produttore, piu' consumatori
|
||||
|
||||
*(vedi slides per grafico)*
|
||||
Introduce la necessita' di un processo gestore:
|
||||
* Il produttore manda i dati sulla porta al gestore
|
||||
* I consumatori mandano un segnale **pronto** al gestore, che quindi invia i dati al consumatore attraverso un canale del processo consumatore.
|
||||
|
||||
*(vedi slides per gestore 'smistatore')*
|
||||
#### Interfaccia produttore
|
||||
|
||||
```
|
||||
produttore {
|
||||
while true {
|
||||
... produce dato d ...
|
||||
send (d) to gestore.dati
|
||||
}
|
||||
}
|
||||
```
|
||||
#### Interfaccia consumatore
|
||||
|
||||
```
|
||||
consumatore {
|
||||
port T dati;
|
||||
.
|
||||
.
|
||||
.
|
||||
send (s) to gestore.pronto
|
||||
p = receive (d) from dati
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Piu' produttori, piu' consumatori
|
||||
|
||||
Caso con **buffer limitato**:
|
||||
|
||||
* I consumatori sono analoghi al caso precedente
|
||||
* I processi produttori sono multipli, quindi devono segnalare allo smistatore che sono pronti a inviare dati. Il buffer limitato fa si che il gestore debba tenere un contatore per controllare che i produttori possano aggiungere dati al buffer.
|
||||
|
||||
Il contatore del gestore viene aumentato quando i dati sono inviati dal produttore, e diminuito quando ci sono consumatori che richiedono dati. I consumatori sono quindi autorizzati a ricevere dati solo se il contatore e' `> 0`, mentre i produttori sono autorizzati a mandare dati solo se il contatore e' `< N` (dove N e' la dimensione del buffer).
|
||||
|
||||
**Questa e' un' applicazione delle guardie**.
|
||||
|
||||
*(vedi slides per gestore)*
|
||||
|
||||
#### Interfaccia produttore
|
||||
|
||||
```
|
||||
produttore {
|
||||
port S OK_send;
|
||||
|
||||
while true {
|
||||
... produce dato d ...
|
||||
send (s) to gestore.pronto
|
||||
receive (s) from OK_send
|
||||
send (d) to gestore.dati
|
||||
}
|
||||
}
|
||||
```
|
||||
### Problema delle strategie di priorita'
|
||||
|
||||
Suppongo un server che gestisca un pool di risorse, e client che fanno richieste per ottenere una risorsa e deve ottenerla quello che ha priorita' massima (gli altri si bloccano mentre sono in attesa).
|
||||
|
||||
*(vedi slides per codice server / esempio di guardie )*
|
||||
|
||||
Le priorita' non sono specificate: le uniche informazioni sono quelle relative alla priorita' dei processi (il primo in coda e' quello con priorita' massima, l'ultimo quello con priorita' minima). Le risorse sono equivalenti.
|
||||
|
||||
#### Interfaccia client
|
||||
|
||||
```
|
||||
client {
|
||||
port int risorsa
|
||||
...
|
||||
send (s) to server.richiesta
|
||||
p = receive (r) from risorsa
|
||||
... usa risorsa...
|
||||
send (r) to server.rilascio
|
||||
}
|
||||
```
|
||||
Il client ha un'interfaccia semplice perche' la priorita' e' tutta gestita dal server.
|
||||
|
||||
### Esercizi
|
||||
|
||||
* l'esercizio 2 e' un crivello di Eratostene (genera tutti i primi filtrando i multipli)
|
||||
* l'esercizio 3 e' un rifacimento dell'esercizio H2O
|
||||
|
150
anno2/YearI/MCAD/lesson13-15112017.md
Normal file
|
@ -0,0 +1,150 @@
|
|||
# Il bar dello stadio
|
||||
|
||||
Analisi di alcune soluzioni proposte del problema "il bar dello stadio".
|
||||
|
||||
## Soluzione Semafori
|
||||
|
||||
```
|
||||
semaphore mutex = 1;
|
||||
semaphore pulizia = 0;
|
||||
semaphore casa = 0;
|
||||
semaphore ospiti = 0;
|
||||
bool pulizia = false;
|
||||
int n_casa = 0;
|
||||
int n_ospiti = 0;
|
||||
int attesa_casa = 0
|
||||
int attesa_ospiti = 0;
|
||||
```
|
||||
```
|
||||
void entra_bar_casa () {
|
||||
P (mutex);
|
||||
if (pulizia || n_casa > NMAX || n_ospiti > 0 || attesa_ospiti > 0) {
|
||||
attesa_casa++;
|
||||
V (mutex);
|
||||
P (casa);
|
||||
attesa_casa--;
|
||||
}
|
||||
n_casa++;
|
||||
if (n_casa < NMAX && attesa_casa > 0 ) {
|
||||
V(casa);
|
||||
} else {
|
||||
V(mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
void esci_bar_casa () {
|
||||
P (mutex);
|
||||
n_casa--;
|
||||
if (pulizia && n_casa == 0 ) {
|
||||
V (pul);
|
||||
} else if (attesa_ospiti > 0 && !pulizia && n_casa == 0) {
|
||||
V (ospiti);
|
||||
} else if (attesa_ospiti == 0 && !pulizia && attesa_casa > 0) {
|
||||
V (casa);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
void entra_bar_ospiti() {
|
||||
P (mutex);
|
||||
if (pulizia || n_ospiti > NMAX ) {
|
||||
attesa_ospiti++;
|
||||
V (mutex);
|
||||
P (ospiti);
|
||||
attesa_ospiti--;
|
||||
}
|
||||
n_ospiti++;
|
||||
if (n_ospiti < NMAX && attesa_ospiti > 0 ) {
|
||||
V(ospiti);
|
||||
} else {
|
||||
V(mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
void esci_bar_ospiti () {
|
||||
P (mutex);
|
||||
n_ospiti--;
|
||||
if (pulizia && n_ospiti == 0 ) {
|
||||
V (pul);
|
||||
} else if (attesa_ospiti > 0 ) {
|
||||
V (ospiti);
|
||||
} else if (attesa_ospiti == 0 && !pulizia && attesa_casa > 0 && n_ospiti == 0) {
|
||||
V (casa);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
void inizio_pulizia () {
|
||||
P (mutex);
|
||||
pulizia = true;
|
||||
if (n_ospiti > 0 || n_casa > 0) {
|
||||
V (mutex);
|
||||
P (pulizia);
|
||||
}
|
||||
V (mutex);
|
||||
}
|
||||
```
|
||||
```
|
||||
void fine_pulizia () {
|
||||
P (mutex);
|
||||
pulizia = false;
|
||||
if (attesa_ospiti > 0) {
|
||||
V (ospiti);
|
||||
} else if (attesa_casa > 0) {
|
||||
V (casa);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
*(la soluzione potrebbe non essere la migliore, implementare di nuovo)*
|
||||
|
||||
## Soluzione Monitor
|
||||
*(parziale, va implementata completamente)*
|
||||
|
||||
```
|
||||
public void apriBar () {
|
||||
chiusura = false;
|
||||
if (!empty(attesa_ospiti)) {
|
||||
signal(attesa_ospiti);
|
||||
} else if (!empty(attesa_casa)) {
|
||||
signal(attesa_casa);
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
public void entra_ospite () {
|
||||
if (chiusura || n_ospiti == NMAX || n_casa > 0 ) {
|
||||
wait(attesa_ospiti);
|
||||
}
|
||||
n_ospiti++;
|
||||
if (n_ospiti < NMAX && !empty(attesa_ospiti)) {
|
||||
signal (attesa_ospiti);
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
public void esci_ospite () {
|
||||
n_ospiti--;
|
||||
if (chiusura && n_ospiti == 0) {
|
||||
signal(pulizia);
|
||||
} else if (n_ospiti == 0 && empty(attesa_ospiti) && !empty(attesa_casa)) {
|
||||
signal(attesa_casa);
|
||||
} else {
|
||||
signal(attesa_ospiti);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Soluzione con scambio di messaggi
|
||||
|
||||
*(implementare)*
|
||||
|
||||
|
||||
|
128
anno2/YearI/MCAD/lesson14-21112017.md
Normal file
|
@ -0,0 +1,128 @@
|
|||
# Introduzione agli Algoritmi Distribuiti
|
||||
|
||||
#### Note organizzative
|
||||
La presenza ad almeno l'80% dei seminari e' richiesta (meglio se tutti). Gruppi di 4.
|
||||
L'argomento trattato e' lo studio di algoritmi distribuiti progettati per l'esecuzione da parte di un insieme di processori autonomi, interconnessi tra loro da un sistema di scambio di messaggi.
|
||||
*(cercare in rete lucidi di N.Lynch relativi al libro)*
|
||||
|
||||
## Algoritmi distribuiti
|
||||
|
||||
Sono algoritmi progettati per lavorare in sistemi distribuiti. Si occupano di:
|
||||
* Comunicazione
|
||||
* Gestione di risorse *(i.e. in mutua esclusione, in ambiente distribuito)*
|
||||
* Sincronizzazione
|
||||
* Consenso
|
||||
|
||||
Considerano:
|
||||
* **attivita' concorrente**
|
||||
* **indeterminatezza nella temporizzazione**, ossia nell'ordine di avvenimento degli eventi. Questo perche' si preferisce avere un protocollo distribuito rispetto ad uno centralizzato (in cui un server centrale puo' agire da vigile). Quindi, non siamo in grado di dire quando un evento accada e quanto tempo richieda.
|
||||
* **possibilita' di errori** (guasti a processori, canali di comunicazione). Questi casi sono gestiti tramite strategie specifiche. **Algoritmi fault-tolerant**.
|
||||
|
||||
Gli algoritmi distribuiti sono difficili da progettare ma soprattutto **difficili da analizzare**, e' quindi difficile stabilire la correttezza e analizzare la complessita' di un AD. La difficolta' e' data soprattutto dall'*interleaving variabile*, ossia dal cambiamento della sequenza di istruzioni da esecuzione a esecuzione.
|
||||
|
||||
Il corso trattera' quindi l'analisi della correttezza formale degli algoritmi, dimostrandola.
|
||||
|
||||
## Modelli di sistemi distribuiti
|
||||
|
||||
I sistemi distribuiti si possono caratterizzare in:
|
||||
* **Modalita' di comunicazione** (memoria comune / messaggi)
|
||||
* **Sincronia / asincronia** di interazioni ed esecuzione
|
||||
* **Fault tolerance**
|
||||
|
||||
#### Sistemi distribuiti con malfunzionamenti
|
||||
|
||||
##### Malfunzionamento di processi
|
||||
|
||||
* **Crash**: bloccato, non manda piu' nulla
|
||||
* **Fallimento bizantino**: guastato ma non bloccato, manda messaggi incongrui.
|
||||
|
||||
##### Malfunzionamento di canali
|
||||
|
||||
* Perdita di messaggi
|
||||
|
||||
### Sistemi distribuiti Sincroni / Asincroni
|
||||
|
||||
In entrambi sistemi non esiste memoria comune.
|
||||
|
||||
#### Sistema Sincroni
|
||||
|
||||
Molto rari, impongono un gran numero di constraint.
|
||||
|
||||
* **Ritardo noto e limitato nella comunicazione**: e' noto un limite di tempo massimo per l'esecuzione di uno **step locale** da parte di un processo. E' anche noto un limite di tempo massimo nella trasmissione del messaggio. Quando questi tempi massimi scadono, si presuppone che nessun messaggio sia stato mandato (non fault tolerant). Viceversa un ambiente fault tolerant cerca di correggere l'errore.
|
||||
|
||||
*(vedi slides per modello sincrono)*
|
||||
|
||||
##### Fasi della computazione
|
||||
|
||||
1. Invio di messaggi a un insieme di processi P
|
||||
2. Ricezione di messaggi da parte dei processi P
|
||||
3. Cambio di stato
|
||||
|
||||
#### Sistemi Asincroni
|
||||
|
||||
Molto vicini ai sistemi di rete attuali.
|
||||
|
||||
* **Trasmissione non sicura**, non si puo' dire quanto un messaggio impiega ad arrivare (o se arriva). Non e' infatti noto alcun limite di tempo massimo, ne' per l'esecuzione ne' per la trasmissione.
|
||||
|
||||
Alcuni problemi **non hanno soluzione asincrona**, specialmente per una situazione falut tolerant o deterministica (che utilizza un algoritmo deterministico). Per questo i sistemi sincroni sono necessari, ma anche algoritmi randomizzati, sistemi che sincronizzino i vari processori, etc.
|
||||
|
||||
#### Descrizione di un algoritmo distribuito
|
||||
|
||||
Occorre specificare:
|
||||
* Grafo di comunicazione
|
||||
* possibilita' di fallimento di processi
|
||||
*(vedi slides per elenco puntato)*
|
||||
|
||||
### Ricerca sugli algoritmi distribuiti
|
||||
|
||||
*(vedi slides)*
|
||||
|
||||
## Reti asincrone
|
||||
|
||||
Due tipi di modelli asincroni:
|
||||
|
||||
* Reti asincrone
|
||||
* Sistemi asincroni con memoria condivisa
|
||||
|
||||
Le reti asincrone effettuano azioni interne ed esterne, eseguite dai processi in comunicazione. L'elemento di comunicazione e' sempre il canale (come per i sincroni), ma in questo caso **i canali possono venire intasati da piu' messaggi**.
|
||||
|
||||
L'obbiettivo e' trovare un formalismo matematico per descrivere processi e canali. Sono detti **reattivi** perche' interagiscono con l'ambiente circostante tramite azioni di input / output. Questo modello e' l'**automa di input/output**.
|
||||
|
||||
### Automi di input / output
|
||||
|
||||
Fornisce un modello matematico generale per componenti reattivi. E' inoltre adatto per descrivere sistemi in modo **modulare**.
|
||||
|
||||
Gli automi di in/out sono definiti da:
|
||||
* un insieme di **azioni** (input / output / interne). Le azioni esterne sono input / output, le azioni locali sono le interne e le output. Tra le azioni di output c'e' *send*, tra le azioni di input *recv*.
|
||||
* un insieme di stati (non necessariamente finito), diviso in stati iniziali, stati di transizione (stati x azioni x stati, prodotto cartesiano).
|
||||
* un insieme di azioni locali (controllate localmente) che rappresenta le azioni **che non possono essere trasposte all'infinito**. Sono dette **tasks**.
|
||||
|
||||
*(vedi slides per esempio grafico)*
|
||||
|
||||
Inoltre:
|
||||
|
||||
* uno **step** di un automa e' un elemento della transizione
|
||||
* un'azione e' detta **abilitata** se esiste uno step `(s, pi, s')` per qualche stato `s'`.
|
||||
|
||||
### Protocollo di consenso
|
||||
|
||||
*(vedi slides per sequenza)*
|
||||
*(vedi slides per implementazione di esempio)*
|
||||
|
||||
### Modelli asincroni: esecuzioni
|
||||
|
||||
Un automa I/O esegue un modello asincrono:
|
||||
* Parte da uno stato *start*
|
||||
* Ripetutamente applica *step* e *trans* fino a uno stato finale
|
||||
|
||||
Essendo che tutte le azioni interne sono pesanti, definiamo **traccia** la sottosequenza delle azioni esterne presenti nelll'esecuzione (studio dal punto di vista dell'osservabilita' esterna). Spesso inoltre l'esecuzione e' divisa in **frammenti**.
|
||||
|
||||
#### Canali
|
||||
|
||||
Anche i canali sono modellati come automi. *(vedi slides per descrizione ed esempio)*
|
||||
|
||||
#### Stati raggiungibili
|
||||
|
||||
Uno stato e' **raggiungibile** se e' presente in una qualche esecuzione. Un **invariante** e' un predicato che e' sempre vero in ogni stato raggiungibile. Gli invarianti sono lo strumento piu' importante per provare le proprieta' degli algoritmi concorrenti e distribuiti.
|
||||
*(vedi slides per esempio di uso dgli invarianti)*
|
||||
|
114
anno2/YearI/MCAD/lesson15-22112017.md
Normal file
|
@ -0,0 +1,114 @@
|
|||
# Primitive Sincrone
|
||||
|
||||
* Send bloccante e receive bloccante
|
||||
*(vedi slides)*
|
||||
|
||||
## Guardie di output
|
||||
|
||||
Una guardia solitamente era trattata come una *recv* bloccante (**guardia di input**), ma possono esistere anche guardie della forma:
|
||||
```
|
||||
(<espr. booleana>); <primitiva send>
|
||||
```
|
||||
Che e' definita come **guardia di output**.
|
||||
**Mescolare guardie di input e guardie di output puo' creare situazioni di deadlock.** *(vedi slides per esempi)*
|
||||
Questo e' dovuto principalmente alla scelta non deterministica tra piu' guardie valide. Per questo motivo si decide di non utilizzare le guardie di output, a parte in alcuni casi specifici.
|
||||
|
||||
## Processi servitori: gestione di pool di risorse equivalenti
|
||||
|
||||
**Caso sincrono**
|
||||
*(slides per implementazione)*
|
||||
|
||||
## Processi servitori: gestione di una risorsa dedicata
|
||||
|
||||
*(vedi slides)*
|
||||
|
||||
## Simulazione di un semaforo
|
||||
|
||||
Le primitive sincrone hanno lo stesso potere di tutte le primitive viste precedentemente. *(vedi slides per implementazione semaforo)*
|
||||
|
||||
## Prod/Cons con buffer di lunghezza finita
|
||||
|
||||
*(slides per implementazione)*
|
||||
|
||||
Il processo buffer e' un server che gestisce inserimento ed estrazione dalla coda.
|
||||
|
||||
Vedi anche esempio con guardie di output, che utilizza primitive di send per mandare i dati al consumatore.
|
||||
|
||||
## Prod / Cons con N processi server
|
||||
|
||||
Il buffer di N dati e' realizzato da N processi dati, produttore e consumatore estrerni.
|
||||
*(slides per implementazione)*
|
||||
Questa soluzione e' semplice, ma presuppone hardware con **elevato parallelismo**.
|
||||
|
||||
## Specifica di strategie di priorita'
|
||||
|
||||
*(slides per implementazione)*
|
||||
I processi aspettano per ottenere una risorsa, ma quando questa si libera viene svegliato il processo con **priorita' piu' alta**. Caso analogo con send asincrona. I clienti in attesa sono inseriti in ordine di priorita' in una coda, e si presuppone un'unica risorsa (non un pool). E' praticamente uguale al caso asincrono.
|
||||
|
||||
## Lettori / Scrittori
|
||||
|
||||
*(slides per implementazione)*
|
||||
Soluzione identica al caso asincrono. In questa soluzione pero' **non e' necessario gestire le code**.
|
||||
|
||||
## Note sulle primitive sincrone
|
||||
|
||||
Difficilmente sono implementate a livello kernel, perche' e' possibile realizzarle con le asincrone.
|
||||
|
||||
# Chiamate di procedura remota e rendez-vous (esteso)
|
||||
|
||||
Chiamata di procedura remota e rendez-vous esteso sono due concetti simili:
|
||||
* Chiamata di procedura remota e' relativa alle soluzioni **multithreading**
|
||||
* Rendez-vous esteso e' relativo alle soluzioni single thread (single server con interrupt)
|
||||
La loro funzione e' pero' la stessa:
|
||||
|
||||
* Il client effettua una richiesta sul server, richiedendo una procedura. Nel caso RPC, questa procedura viene eseguita su un processo (thread) separato. Nel caso del rendez-vous esteso, l'operazione richiesta e' specificata come una serie di istruzioni che possono comparire in un punto qualunque del server. Il server usa quindi un *accept* che interrompe l'esecuzione e passa all'esecuzione delle istruzioni. *(vedi slides per dettagli e implementazione)*
|
||||
|
||||
### Esempio RPC: servizio di sveglia
|
||||
|
||||
Insieme di N processi clienti possono richiedere di essere svegliati da un processo servitore, dopo un tempo da loro prefissato. *(slides per grafico)*
|
||||
|
||||
Il server utilizzera' quindi un clock, che opera su un processo separato, e una serie di N thread relativi ai singoli processi, che sono spawnati una volta che il clock raggiunge i tempi prefissati. I thread si occupano di svegliare i processi.
|
||||
|
||||
*(slides per implementazione)*
|
||||
|
||||
### Rendez-vous
|
||||
|
||||
Il servizio richiesto e' specificato come insieme di istruzioni che possono comparire in un punto qualunque del processo servitore:
|
||||
|
||||
```
|
||||
accept<servizio> (in <param-ingresso>,
|
||||
out <param-uscita>);
|
||||
(S1, ... , SN) -> S0;
|
||||
```
|
||||
|
||||
* Nota: S1, ... SN sono servizi da eseguire in rendez-vous, mentre S0 e' una procedura eseguita solo dal server, non in rendez-vous.
|
||||
|
||||
#### Accept
|
||||
|
||||
* Bloccante: se non sono presenti richieste, provoca la sospensione del server
|
||||
* Se lo stesso servizio e' richiesto da piu' processi, si utilizza una coda FIFO
|
||||
* Lo schema di comunicazione e' quindi **asimmetrico da molti a uno**.
|
||||
|
||||
*(slides per grafico di esecuzione)*
|
||||
|
||||
##### Selezione delle richieste in base ai parametri di ingresso
|
||||
|
||||
Con accept, la selezione della richiesta da servire **puo' dipendere anche dai parametri d'ingresso della richiesta stessa**. In questo caso la guardia logica che condiziona l'esecuzione dell'azione puo' essere espressa anche in termini dei parametri di ingresso.
|
||||
Questo richiede una doppia interazione tra server e client, la prima per trasmettere i parametri, la seconda per la richiesta stessa.
|
||||
*NB: il numero di parametri deve essere limitato*
|
||||
|
||||
#### Esempio: sveglia
|
||||
|
||||
In questo esempio con rendez-vous del processo sveglia ci sono 3 tipi di richieste: *(vedi slides)*
|
||||
*(slides per implementazione)*
|
||||
In questo esempio, il client manda due richieste: nella prima manda il tempo dopo cui vuole essere svegliato, nella seconda manda l'effettiva richiesta di sveglia. Il server riceve la prima richiesta e la memorizza. Quando riceve la seconda richiesta, riordina il vettore *tempo-di-sveglia* in cui ha inserito il tempo indicato, e sveglia il processo dopo il tempo ricevuto.
|
||||
|
||||
#### Esempio: r&w
|
||||
|
||||
I lettori fanno tutte le *call*, gli scrittori fanno tutti gli *accept*. *(vedi slides per implementazione)*
|
||||
|
||||
|
||||
# Linguaggi CSP: Communication Sequential Process
|
||||
|
||||
Modello di interazione tra processi, che utilizzano una comunicazione sincrona (di solito sono sulla stessa board, vari processori). *(vedi slides)*
|
||||
|
4
anno2/YearI/MCAD/lesson16-28112017.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Esercitazione - Scambio di messaggi
|
||||
|
||||
1. scambio di messaggi - bar dello stadio. Importante e' dividere la richiesta di ingresso dall'ingresso in se, oltre che dividere uscita e ingresso. *reimplementare*
|
||||
2.
|
28
anno2/YearI/MCAD/lesson2-04102017.md
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Programmazione Concorrente
|
||||
|
||||
## Specifica della Concorrenza (pre-thread)
|
||||
|
||||
Vari modi di esprimere la concorrenza: (vedi slides per esempi)
|
||||
(ordine circa cronologico, dal piu' vecchio)
|
||||
|
||||
* fork / join : non molto leggibili, molto flessibili, basso livello
|
||||
|
||||
* cobegin / coend : approccio modulare contenente un "blocco" di codice parallelo, di piu' facile gestione. >Tutte le istruzioni all'interno del blocco sono eseguite in parallelo, e possono contenere strutture cobegin/coend. Facile lettura, ma molto vincolante: e' molto difficile avere informazioni sullo stato del programma all'interno del blocco begin/end.
|
||||
|
||||
* processo : unita' parallele identificate da parola chiave (*process* in Pascal, *task* in ADA). Sono moduli contenenti un body che viene eseguito in parallelo. Si puo' usare in concomitanza a cobegin/coend.
|
||||
|
||||
### Differenza tra Processi SO e processi di Linguaggi Concorrenti
|
||||
|
||||
Nel kernel, ogni processo ha un nome, occupa una certa quantita' di memoria, viene eseguito da un linuguaggio sequenziale. Un linguaggio concorrente, al contrario, a un programma puo' associare piu' task (creati dalle primitive, all'interno della runtime del linguaggio). Il SO (kernel) vedra' comunque ogni programma come unico processo di sistema.
|
||||
|
||||
(vedi slides per schemi)
|
||||
|
||||
## Processi e Thread
|
||||
|
||||
I processi possono essere caratterizzati in processi *leggeri* (thread) e classici. (vedi slides per ripasso sui thread)
|
||||
(vedi slides per confronto SO/concorrenza)
|
||||
|
||||
## Simulazione di Programmi Concorrenti
|
||||
|
||||
Un simulatore di concorrenza controlla l'interleaving di azioni atomiche di un programma concorrente. (BACI, jBACI, SPIN)
|
||||
|
124
anno2/YearI/MCAD/lesson3-04102017.md
Normal file
|
@ -0,0 +1,124 @@
|
|||
# Primitive di Sincronizzazione
|
||||
|
||||
## Semafori
|
||||
|
||||
Dijkstra: I semafori un tipo di dato astratto (puo' essere considerato un oggetto), che assume solo valori interi >= 0, su cui si eseguono solo **DUE** operazioni atomiche (P = wait, V = signal).
|
||||
|
||||
### Semantica di P e V:
|
||||
|
||||
--- P
|
||||
void P (semaphore S) {
|
||||
<< when (val(S) > 0) val(S)--; >>
|
||||
}
|
||||
|
||||
--- V
|
||||
void V (semaphore S) {
|
||||
<< val(S)++; >>
|
||||
}
|
||||
|
||||
<<...>> vuol dire "eseguito in sezione critica"
|
||||
|
||||
Nota: ci sono implementazioni semaforiche che permettono al semaforo di assumere valori negativi, in questo modo il numer o di processi in attesa e' dato dal valore <0
|
||||
|
||||
### Atomicita' di P e V
|
||||
|
||||
L'atomicita' in ambiente monoprocessore si ottiene disabilitando l'interrupt ed effettuando la chiamata. Questo pero' non basta in architetture multiprocessore a memoria condivisa: la granularita' dell'atomicita' e' infatti diversa.
|
||||
|
||||
Per questo motivo, in architetture multiprocessore si usano procedure di *test and set* per creare operazioni di *lock e unlock*.
|
||||
|
||||
### Invariante semaforico
|
||||
|
||||
Un invariante semaforico e' un'uguaglianza sempre vera associata a un semaforo, che rimane per tutta la durata di vita del semaforo.
|
||||
|
||||
### Terminologia
|
||||
|
||||
* val(sem,t) : valore del semaforo all'istante t
|
||||
* I(sem) : valore iniziale del semaforo
|
||||
* nP(sem, t) : volte in cui e' stata eseguita **con successo** l'operazione P su sem all'istante t
|
||||
* nV(sem, t) : volte in cui e' stata eseguita **con successo** l'operazione V su sem all'istante t
|
||||
|
||||
Vale quindi:
|
||||
```
|
||||
val (sem, t) = I + nV(sem,t) - nP(sem,t) <- V incrementa, P decrementa
|
||||
|
||||
nP(sem, t) <= I + nV (sem, t) <- Invariante semaforico
|
||||
```
|
||||
|
||||
### Problema della mutua esclusione - Soluzioni
|
||||
|
||||
Uno e un solo processo puo' avere accesso alla sezione critica.
|
||||
|
||||
Una buona soluzione deve soddisfare le proprieta' classiche:
|
||||
1. Garanzia della mutua esclusione
|
||||
2. Non correlazione : un processo che non deve accedere alla SC (sezione critica) e che ne e' al di fuori **non deve influenzare** processi che tentano di accedere alla sezione critica.
|
||||
3. Assenza di Deadlock : se la sezione critica e' libera, e vari processi vogliono entrare, in un tempo finito uno deve entrare
|
||||
4. Assenza di Starvation : se un processo vuole entrare nella sezione critica, l'attesa dev'essere **finita**.
|
||||
|
||||
#### Soluzione Semaforica - MUTEX
|
||||
|
||||
Se OP e' una operazione su una variabile condivisa:
|
||||
```
|
||||
semaphore mutex = 1
|
||||
P1
|
||||
.
|
||||
.
|
||||
P(mutex)
|
||||
OP
|
||||
V(mutex)
|
||||
```
|
||||
Ogni operazione critica deve essere preceduta da un P(mutex) e un V(mutex).
|
||||
|
||||
##### Dimostrazione della soluzione mutex
|
||||
|
||||
Si vuole mostrare che la soluzione mutex e' ottimale al problema della mutua esclusione.
|
||||
Dimostreremo ogni proprieta' analizzata:
|
||||
|
||||
1. Garanzia di ME
|
||||
|
||||
ncs(t) : numero di processi che al tempo t operano in sezione critica
|
||||
|
||||
* Essendo che ogni processo esegue obbligatoriamente una P e una V (**Invariante topologico**):
|
||||
|
||||
```
|
||||
ncs(t) = nP(mutex,t) - nV (mutex, t)
|
||||
|
||||
```
|
||||
* Inoltre, su mutex vale l'invariante semaforico
|
||||
|
||||
```
|
||||
nP(mutex, t) <= 1+nV(mutex,t) --> ncs(t) <= 1
|
||||
```
|
||||
|
||||
* Utilizzando l'invariante topologico:
|
||||
```
|
||||
nV(mutex, t) <= nP(mutex, t) --> ncs(t) >= 0
|
||||
```
|
||||
* quindi:
|
||||
```
|
||||
0 <= ncs <= 1
|
||||
```
|
||||
* dimostrato
|
||||
|
||||
|
||||
2. No deadlock
|
||||
|
||||
* Dimostrazione per assurdo: esiste un istante t in cui c'e' deadlock
|
||||
```
|
||||
val(mutex, t) = 0 && ncs(t) = 0
|
||||
```
|
||||
* dato che il val di mutex e' 0
|
||||
|
||||
```
|
||||
1+nV(mutex, t) - nP(mutex,t) = 0
|
||||
```
|
||||
* ma questo entra in collisione con la definizione di ncs:
|
||||
|
||||
```
|
||||
nP(mutex, t) - nV(mutex,t) = ncs(t) = 0
|
||||
```
|
||||
* per assurdo, dimostrato (1 != 0)
|
||||
|
||||
3. No starvation
|
||||
|
||||
Non dimostrabile, non conoscendo l'implementazione del semaforo (FIFO queue e' fair, ma non e' l'unica implementazione)
|
||||
|
278
anno2/YearI/MCAD/lesson4-10102017.md
Normal file
|
@ -0,0 +1,278 @@
|
|||
# Generalizzazione al problema della Mutua Esclusione
|
||||
|
||||
Esempio: strada a senso unico alternato. Piu' mezzi possono passare nella stessa direzione, ma non due mezzi in senso inverso.
|
||||
|
||||
## Soluzione al problema: mutex
|
||||
|
||||
Abbiamo un numero N di operazioni su sezione critica OP1, OP2, ... OPi, OPN con N>1.
|
||||
|
||||
Utilizziamo un semaforo mutex e un semaforo per ogni OPi (Mi), assieme a un contatore count_i:
|
||||
semaphore mutex = 1
|
||||
semaphore Mi = 1 <- Mi e' utilizzato per proteggere count_i
|
||||
int count_i = 0
|
||||
|
||||
```
|
||||
public void OPi() {
|
||||
P(Mi);
|
||||
count_i++;
|
||||
if (count == 1) { P(mutex); } // il primo processo che ha cercato di entrare con la risorsa occupata e' fermo su mutex
|
||||
V(Mi);
|
||||
.
|
||||
.
|
||||
OPi
|
||||
.
|
||||
.
|
||||
P(Mi);
|
||||
count_i--;
|
||||
if (count_i == 0) { V(mutex); }
|
||||
V(Mi);
|
||||
}
|
||||
```
|
||||
Esempio:
|
||||
```
|
||||
public void da_nord() {
|
||||
P(Mnord);
|
||||
count_nord++;
|
||||
if(count_nord == 1) P(mutex);
|
||||
V(Mnord);
|
||||
.
|
||||
.
|
||||
Transito da nord
|
||||
.
|
||||
.
|
||||
P(Mnord);
|
||||
count_nord--;
|
||||
if (count_nord == 0) V(mutex);
|
||||
V(Mnord);
|
||||
}
|
||||
```
|
||||
Questo e' un esempio del problema di **Writers & Readers** (Lettori e scrittori), dove su un file condiviso i lettori possono leggere in contemporanea ma gli scrittori devono lavorare uno alla volta.
|
||||
|
||||
## Readers & Writers (lettori e scrittori)
|
||||
|
||||
* Readers:
|
||||
|
||||
semaphore mutex = 1
|
||||
semaphore legge = 1
|
||||
int count = 0
|
||||
```
|
||||
public void lettura () {
|
||||
|
||||
P(legge);
|
||||
count++;
|
||||
if (count == 1) P(mutex);
|
||||
V(legge);
|
||||
.
|
||||
leggi
|
||||
.
|
||||
P(leggi)
|
||||
count--;
|
||||
if (count == 0) V(mutex);
|
||||
V(leggi);
|
||||
}
|
||||
```
|
||||
* Writers:
|
||||
|
||||
```
|
||||
public void scrivi () {
|
||||
P(mutex);
|
||||
.
|
||||
scrivi
|
||||
.
|
||||
V(mutex);
|
||||
}
|
||||
```
|
||||
|
||||
Questa soluzione e' soggetta a **starvation** da parte degli scrittori, perche' se continuano ad arrivare lettori ininterrottamente, non c'e' garanzia che i writers riescano ad ottenere la risorsa.
|
||||
|
||||
### Dimostrazione della correttezza - r&w
|
||||
|
||||
* Se non ci fossero lettori, varrebbe l'invariante semaforico per mutex:
|
||||
```
|
||||
nP(mutex, t) - nV(mutex, t) = nWriters (se nReaders = 0);
|
||||
```
|
||||
* Se ci sono lettori:
|
||||
```
|
||||
nP(mutex, t) - nV(mutex, t) = nWriters + 1 (se nReaders != 0);
|
||||
```
|
||||
* Posso quindi affermare:
|
||||
```
|
||||
0 <= nP(mutex, t) - nV(mutex, t) <= 1
|
||||
```
|
||||
* Se non ci sono lettori, ottengo:
|
||||
```
|
||||
0 <= nWriters <= 1 (Che e' corretto, in quanto con un semaforo binario puo' esserci al piu' uno scrittore)
|
||||
```
|
||||
* Se ci sono lettori:
|
||||
```
|
||||
nWriters + 1 <= 1 -> nWriters = 0 (corretto, non possono esserci lettori e scrittori contemporaneamente)
|
||||
```
|
||||
* La soluzione e' quindi corretta.
|
||||
|
||||
## Semafori per sincronizzazione tra processi
|
||||
|
||||
Supponiamo due processi:
|
||||
* P1, che deve effettuare operazione A
|
||||
* P2, che deve effettuare operazione B
|
||||
|
||||
E pongo che A dev'essere eseguita **dopo** B.
|
||||
Un modo e' introdurre un semaforo:
|
||||
|
||||
semaphore sync = 0;
|
||||
```
|
||||
P1 P2
|
||||
. .
|
||||
. .
|
||||
P(sync) B
|
||||
A V(sync)
|
||||
. .
|
||||
. .
|
||||
```
|
||||
|
||||
#### Dimostrazione di correttezza
|
||||
|
||||
Supponiamo che A venga eseguito *prima* di B. (**Per Assurdo**).
|
||||
Avro' quindi:
|
||||
```
|
||||
(sequenza temporale)
|
||||
--P(sync)--A-----B--V(sync)--
|
||||
```
|
||||
* Che pero' vorrebbe dire:
|
||||
```
|
||||
nP (sync, t) = 1
|
||||
nV (sync, t) = 0
|
||||
nP (sync, t) <= nV (sync, t) <- NON E' POSSIBILE
|
||||
```
|
||||
* dimostrato
|
||||
|
||||
### Problema del rendez-vous
|
||||
|
||||
Due processi che si devono aspettare per effettuare una determinata operazione.
|
||||
|
||||
semaphore sem1 = 0;
|
||||
semaphore sem2 = 0;
|
||||
|
||||
```
|
||||
P1 P2
|
||||
. .
|
||||
. .
|
||||
A1 B1
|
||||
V(sem1) V(sem2)
|
||||
P(sem2) P(sem1)
|
||||
A2 B2
|
||||
. .
|
||||
```
|
||||
Qui, i due processi segnalano al concorrente che hanno effettuato l'operazione A1/B1, e quindi proseguono con A2/B2.
|
||||
|
||||
## Produttore e Consumatore
|
||||
|
||||
### 1 Produttore, 1 consumatore (caso semplice = buffer unitario)
|
||||
|
||||
Vogliamo che il produttore riempia un buffer solo quando il consumatore l'ha consumato.
|
||||
Il consumatore non deve prelevare due volte lo stesso dato.
|
||||
|
||||
Due semafori:
|
||||
semaphore empty = 1 // notifica che il buffer e' vuoto (vuoto a inizializzazione)
|
||||
semaphore full = 0 // notifica che il buffer e' pieno
|
||||
|
||||
* Inserimento (producer)
|
||||
```
|
||||
void invio (dato T) {
|
||||
P(empty);
|
||||
...inserisco dato...
|
||||
V(full);
|
||||
}
|
||||
```
|
||||
* Estrazione (consumer)
|
||||
```
|
||||
T estrai () {
|
||||
T dato;
|
||||
P(full);
|
||||
...
|
||||
dato = datoestratto;
|
||||
...
|
||||
V(empty);
|
||||
return dato;
|
||||
}
|
||||
```
|
||||
Nota: le operazioni di estrazione e inserimento non sono atomiche, e' necessario dimostrare che non serve utilizzare un mutex per proteggere l'accesso ed evitare la mutua esclusione.
|
||||
|
||||
#### Dimostrazione di correttezza (p&c)
|
||||
|
||||
nd(t) = numero totale di operazioni di deposito (produttore inserisce)
|
||||
ne(t) = numero totale di estrazioni (consumatore estrae)
|
||||
|
||||
* Quindi, io posso al massimo fare un deposito in piu' di un'estrazione:
|
||||
```
|
||||
nd(t) <= ne(t) + 1
|
||||
ne(t) <= nd(t) // non posso estrarre se non ho depositato
|
||||
```
|
||||
* Posso riscrivere le due operazioni come:
|
||||
```
|
||||
0 <= nd(t) - ne(t) <= 1 // e' quello che voglio dimostrare
|
||||
```
|
||||
* Nota: queste procedure (estrazione, deposito) possono essere chiamate piu' volte in sequenza. Posso pero' affermare che il deposito e' effettuato solo dal produttore, che utilizza la procedura sopra illustrata. Quindi:
|
||||
```
|
||||
nV(full,t) <= nd(t) <= nP(empty,t)
|
||||
nV(empty,t) <= ne(t) <= nP(full,t) // le due disuguaglianze sono simmetriche
|
||||
```
|
||||
* A questo posso aggiungere l'invariante semaforico:
|
||||
```
|
||||
ne(t) <= nP(full,t) <= nV(full,t) <= nd(t) <= nP(empty) <= nV(empty,t) + 1 <= ne(t) + 1
|
||||
```
|
||||
* Ottengo:
|
||||
```
|
||||
ne(t) <= nd(t) <= ne(t)+1
|
||||
```
|
||||
* Da cui:
|
||||
```
|
||||
0 <= nd(t) - ne(t) <= 1
|
||||
```
|
||||
* Che quindi dimostra la correttezza della procedura
|
||||
|
||||
#### Producer & Consumer - Mutua esclusione (buffer unitario)
|
||||
|
||||
**Per Assurdo:**
|
||||
Esiste un'istante T in cui il consumatore estrae e il produttore deposita (il che violerebbe il principio di mutua esclusione).
|
||||
|
||||
* Dal codice:
|
||||
```
|
||||
nP(empty, T) = 1 + nV(full, T) -> nV(full, T) = nP(empty, T) - 1
|
||||
nP(full, T) = 1 + nV(empty, T)
|
||||
```
|
||||
* Unendo le due con l'invariante semaforico:
|
||||
```
|
||||
nP(empty, T) <= nV(empty, T) + 1 <= nP(full, T) <= nV(full, T) <= nP(empty, T)
|
||||
```
|
||||
* Ho quindi dimostrato che:
|
||||
```
|
||||
nP(empty, T) <= nP(empty,t) - 1 // IMPOSSIBILE
|
||||
```
|
||||
* per assurdo, dimostrato
|
||||
|
||||
Questo dimostra che non e' necessario proteggere il buffer con un mutex dato che la soluzione non viola **mai** il processo di mutua esclusione, ma solo nel caso in cui il buffer sia unitario. Il caso di buffer circolare non e' considerato, potrebbe essere necessario un mutex.
|
||||
|
||||
## Semafori e Sezioni critiche condizionali
|
||||
|
||||
Spesso, se una struttura condivisa deve essere utilizzata da piu' processi, si crea una condizione da controllare che e' unica alla sezione critica. Questo vuol dire che un processo deve controllare la condizione accedendo alla sezione critica, ma questo potrebbe non essere possibile (nel caso questa fosse bloccata da un altro processo).
|
||||
|
||||
Consideriamo una regione R:
|
||||
La procedura implica entrare nella sezione critica, verificare C e se vera eseguire S, poi liberare la sezione critica.
|
||||
Se C non e' verificata, il processo deve invece uscire e aspettare.
|
||||
```
|
||||
region R << when(C) S; >>
|
||||
```
|
||||
Abbiamo quindi la situazione detta di **attesa attiva**:
|
||||
1. Processo entra nella sezione critica
|
||||
2. Testa la condizione: se e' vera, esegue la procedura ed esce
|
||||
3. Se la condizione e' falsa, deve uscire e tornare a 1
|
||||
|
||||
### Soluzione mutex
|
||||
|
||||
Quando il processo trova una condizione falsa nella sezione critica, dovrebbe tornare all'inizio, rientrare e testare la condizione. Nel peggiore dei casi, tutte le volte che un processo esegue un'operazione S con successo, cambia i valori della condizione C. In questo modo posso utilizzare un semaforo per bloccare il ritorno al test.
|
||||
|
||||
1. Processo entra nella sezione critica
|
||||
2. Testa la condizione: se e' vera, esegue la procedura ed esce
|
||||
3. Se la condizione e' falsa, deve uscire e aspettare V(sem) per tornare a 1
|
||||
|
||||
In questo modo l'attesa non e' piu' attiva, e il processo ha piu' possibilita' di entrare nella sezione critica.
|
177
anno2/YearI/MCAD/lesson5-11102017.md
Normal file
|
@ -0,0 +1,177 @@
|
|||
# Semafori e Sezioni critiche condizionali (continua)
|
||||
|
||||
## Implementazione di Accesso condizionale
|
||||
|
||||
Inizializzazione:
|
||||
```
|
||||
struttura dati R;
|
||||
semaphore mutex = 1;
|
||||
semaphore sem = 0; //blocca accesso a sc
|
||||
int csem = 0; //contatore per #processi bloccati su sem
|
||||
```
|
||||
Due operazioni: **Richiesta** e **Rilascio**
|
||||
|
||||
### Soluzione classica
|
||||
|
||||
#### Richiesta
|
||||
|
||||
Un processo cerca di entrare nella SC, verificare la condizione (C) ed eseguire l'operazione
|
||||
```
|
||||
public void request () {
|
||||
// ingresso in SC
|
||||
P (mutex);
|
||||
|
||||
// verifica della condizione
|
||||
// finche' non e' verificata, loop
|
||||
while (!C) {
|
||||
csem++;
|
||||
V (mutex); // libero la SC
|
||||
P (sem); // aspetto che sem sia libero
|
||||
P (mutex); // rientro in SC
|
||||
}
|
||||
|
||||
// se arriva qui, C e' verificata e il processo e' nella sezione critica
|
||||
<< operazione critica >>
|
||||
V (mutex)
|
||||
}
|
||||
```
|
||||
**NOTA**: E' possibile che un processo entri nella sezione critica mentre l'altro e' dentro il loop while. Questo fenomeno si chiama *spiffero*.
|
||||
|
||||
#### Rilascio
|
||||
|
||||
Rilascio delle risorse dopo aver eseguito l'operazione critica.
|
||||
```
|
||||
public void release () {
|
||||
P (mutex);
|
||||
<< operazione critica >>
|
||||
if (csem > 0) {
|
||||
csem--;
|
||||
V (sem);
|
||||
}
|
||||
V (mutex);
|
||||
}
|
||||
```
|
||||
|
||||
### Soluzione alternativa
|
||||
|
||||
Questa soluzione, per quanto piu' efficiente,**e' utilizzabile solo quando l'operazione critica cambia il valore della condizione C, permettendo a un altro processo di accedervi**. C deve essere quindi condivisa tra processi.
|
||||
|
||||
Due operazioni, come nella soluzione classica.
|
||||
#### Richiesta
|
||||
```
|
||||
public void request () {
|
||||
|
||||
if (!C) { // simile alla soluzione classica
|
||||
csem++; // niente loop perche' assumiamo che eseguire l'OC cambi il valore di C
|
||||
V (mutex);
|
||||
P (sem);
|
||||
csem--; // V (sem) puo' essere chiamato qui, o durante il rilascio
|
||||
}
|
||||
|
||||
<< operazione critica >>
|
||||
V (mutex);
|
||||
}
|
||||
```
|
||||
#### Rilascio
|
||||
```
|
||||
public void release () {
|
||||
P (mutex);
|
||||
<< operazione critica >>
|
||||
if (csem > 0) {
|
||||
V (sem);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
*(vedi esempio con pool di risorse)*
|
||||
|
||||
## Producer and Consumer (produttore e consumatore) - Semaforo contatore
|
||||
|
||||
Si presenta qui una soluzione alternativa al problema del produttore e consumatore, che utilizza un semaforo contatore per gestire un buffer **circolare**;
|
||||
Si assume 1 produttore e 1 consumatore.
|
||||
|
||||
### Inizializzazione
|
||||
```
|
||||
semaphore empty = N;
|
||||
semaphore full = 0;
|
||||
T buffer[N];
|
||||
int head = 0;
|
||||
int tail = 0;
|
||||
```
|
||||
N e' la dimensione del buffer, e empty e' inizializzato ad essa perche' tutte le celle del buffer sono vuote all'inizializzazione. *head* e *tail* sono due contatori, *tail* per il producer e *head* per il consumer, che indicano la cella occupata.
|
||||
|
||||
#### Producer (invio di dati)
|
||||
```
|
||||
public void send (T data) {
|
||||
P (empty);
|
||||
buffer[tail] = data;
|
||||
tail = (tail++) % N;
|
||||
V (full);
|
||||
}
|
||||
```
|
||||
|
||||
#### Consumer (ricezione di dati)
|
||||
```
|
||||
public T receive () {
|
||||
P (full);
|
||||
T data = buffer[head];
|
||||
head = (head++) % N;
|
||||
V (empty);
|
||||
return data;
|
||||
}
|
||||
```
|
||||
Si nota come il buffer circolare implichi gli incrementi di *head* e *tail* in modulo N.
|
||||
|
||||
### Dimostrazione di Correttezza
|
||||
|
||||
* Siano:
|
||||
```
|
||||
nd(t) = # inserimenti (depositi) da parte del produttore
|
||||
ne(t) = # estrazioni (ricezioni) da parte del consumatore
|
||||
```
|
||||
* La precedente dimostrazione del problema producer-consumer aveva portato:
|
||||
```
|
||||
0 <= nd(t) - ne(t) <= 1
|
||||
```
|
||||
* Perche' non possono esserci piu' estrazioni che inserzioni, e le inserzioni devono essere al massimo 1 in piu' delle estrazioni.
|
||||
|
||||
* Da qui, utilizzando l'invariante semaforico:
|
||||
```
|
||||
NP (empty, t) <= NV (empty, t) + N
|
||||
```
|
||||
* il che rispecchia l'inizializzazione del problema.
|
||||
|
||||
* Per dimostrare la correttezza, supponiamo che esista un istante t in cui il producer deposita e il consumer estrae. Dobbiamo quindi dimostrare che **head != tail**, in quanto non e' possibile che producer e consumer lavorino sulla stessa cella del buffer.
|
||||
|
||||
* Siano:
|
||||
```
|
||||
p1(t) = # incrementi in coda (tail)
|
||||
p2(t) = # incrementi in testa (head)
|
||||
```
|
||||
* Da questo, la tesi puo' essere riscritta come:
|
||||
```
|
||||
1 <= p1(t) - p2(t) <= N-1
|
||||
```
|
||||
* Supponendo che entrambi i processi debbano ancora uscire dalla sezione critica:
|
||||
```
|
||||
p1(t) = NV(full, t) = NP (empty, t) - 1
|
||||
p2(t) = NV(empty, t) = NP (full, t) - 1
|
||||
```
|
||||
* Da cui si puo' utilizzare l'invariante semaforico per ottenere:
|
||||
```
|
||||
p2(t) = NP(full,t) - 1 <= NV(full, t) - 1 <= p1(t) - 1 <= NP(empty, t) - 2 <= NV(empty, t) - 2 <= NV(empty, t) - 2 + N <= p2(t) + N - 2
|
||||
```
|
||||
* Considerare solo i termini p1, p2:
|
||||
```
|
||||
p2(t) <= p1(t) - 1 <= p2 (t) + N - 2
|
||||
```
|
||||
* Possiamo sommare 1 a tutti i termini senza cambiare la validita' della disequazione:
|
||||
```
|
||||
p2(t) + 1 <= p1(t) <= p2(t) + N - 1
|
||||
```
|
||||
* Che, sottraendo a ogni termine p2(t), risulta:
|
||||
```
|
||||
1 <= p1(t) - p2(t) <= N - 1
|
||||
```
|
||||
* Che e' proprio la tesi di partenza della dimostrazione di correttezza. CVD
|
183
anno2/YearI/MCAD/lesson7-18102017.md
Normal file
|
@ -0,0 +1,183 @@
|
|||
# Semafori concorrenti - HHO
|
||||
*(scrivi il resto)*
|
||||
## No deadlock
|
||||
|
||||
Per assurdo, supponiamo deadlock al tempo T: i semafori si bloccano su P (idrogeno su P(sH), ossigeno su uno dei P(sO))
|
||||
|
||||
* Invarianti semaforici
|
||||
```
|
||||
val(sH,T) = 0
|
||||
=> NP(sO, T) = NV(s0,T) // nessuna P eseguita con successo, il valore di sH e' 0 perche' il processo e' bloccato
|
||||
|
||||
val(sO,T) = 0
|
||||
=> NP(sH, T) = NV(sH, T) + 2
|
||||
```
|
||||
|
||||
* utilizziamo gli invarianti topologici
|
||||
```
|
||||
NP (sH, T) = NV (sO, T)
|
||||
=> 0 <= NP(sO,T) - NV (sH,T) <= 1
|
||||
```
|
||||
|
||||
* Cerchiamo un assurdo:
|
||||
```
|
||||
NV (sH, T) + 2 = NP (sH, T) = NV (sO, T) <= NV (sH,T) + 1
|
||||
```
|
||||
* Il risultato non e' ammissibile (2<1) quindi non e' possibile ottenere deadlock dalla soluzione presentata.
|
||||
|
||||
# Uso di Semafori Privati
|
||||
|
||||
Semafori (binari/contatori) di **proprieta' di uno o piu' processi**.
|
||||
I proprietari sono gli unici a poter eseguire P sul suddetto semaforo, ma **non ci sono restrizioni sull'uso di V**.
|
||||
|
||||
## Utilizzi
|
||||
|
||||
* Svegliare un determinato processo, per rendere la gestione di una coda di processi indipendente dall'implementazione del semaforo (kernel space). In questo modo posso stabilire una gerarchia di prorita' tra processi.
|
||||
|
||||
### Vantaggi
|
||||
|
||||
Precisione nella gestione di pool di processi.
|
||||
|
||||
### Svantaggi
|
||||
|
||||
Complessita' aggiunta all'implementazione: il programma deve mantenere piu' semafori (anche a livello di kernel: i semafori privati possono essere implementati **al di fuori** di una coda). Il caso piu' comune e' la necessita' di dover mantenere un semaforo per ogni processo in coda, invece che uno solo (e una coda).
|
||||
|
||||
## Schemi di gestione
|
||||
|
||||
### Schema 1
|
||||
|
||||
Contesto:
|
||||
```
|
||||
< struttura dati gestore >
|
||||
semaphore mutex;
|
||||
semaphore priv[n] = {0}; // array di semafori privati, ognuno dedicato a un processo (n processi)
|
||||
```
|
||||
|
||||
#### Procedura di acquisizione
|
||||
```
|
||||
public int acquisisci (int p) {
|
||||
// simile ai semafori condizionali, anche qui dovremo testare l'ottenibilita' di una risorsa
|
||||
int j; // risorsa da allocare
|
||||
P (mutex);
|
||||
if ( ! Cp) { // Cp e' la condizione per il processo p
|
||||
< registrare sospensione del processo p >
|
||||
V (mutex);
|
||||
P (priv[p]);
|
||||
< registro che il processo p non e' piu' sospeso >
|
||||
}
|
||||
< alloco risorsa j >
|
||||
V (mutex);
|
||||
return j;
|
||||
}
|
||||
```
|
||||
#### Procedura di rilascio
|
||||
```
|
||||
public void rilascio (int j) {
|
||||
int p;
|
||||
P (mutex);
|
||||
< registro rilascio risorsa j >
|
||||
if ( < esiste almeno 1 processo sospeso > ) {
|
||||
< scelgo il processo p con massima priorita' >
|
||||
V (priv[p]);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
### Esempio: allocazione di pool di risorse
|
||||
|
||||
```
|
||||
int avail = M;
|
||||
bool libero[M] = {true};
|
||||
bool sospeso[n] = {false};
|
||||
int cnts = 0; // conta i sospesi
|
||||
semaphore mutex;
|
||||
semaphore priv[n] = {0}; // array di semafori privati, ognuno dedicato a un processo (n processi)
|
||||
```
|
||||
#### Procedura di acquisizione
|
||||
```
|
||||
public int acquisisci (int p) {
|
||||
int j; // risorsa da allocare
|
||||
P (mutex);
|
||||
if ( avail == 0) {
|
||||
sospeso[p] = true;
|
||||
cnts++;
|
||||
V (mutex);
|
||||
P (priv[p]);
|
||||
sospeso[p] = false;
|
||||
cnts--;
|
||||
}
|
||||
avail--;
|
||||
while (libero[j]) j++;
|
||||
libero[i] = false;
|
||||
V (mutex);
|
||||
return j;
|
||||
}
|
||||
```
|
||||
#### Procedura di rilascio
|
||||
```
|
||||
public void rilascio (int j) {
|
||||
int p;
|
||||
P (mutex);
|
||||
avail++;
|
||||
libero[j] = true;
|
||||
if ( cnts > 0 ) {
|
||||
p = maxpriority (sospeso); // ottengo il processo con priorita' massima
|
||||
V (priv[p]);
|
||||
} else {
|
||||
V (mutex);
|
||||
}
|
||||
}
|
||||
```
|
||||
### Schema 2
|
||||
|
||||
Siccome e' complesso dal punto di vista della programmazione strutturata (alto livello), e' stato proposto un secondo schema.
|
||||
|
||||
#### Procedura di acquisizione
|
||||
```
|
||||
public void acquisizione (int p) {
|
||||
P (mutex);
|
||||
if (Cp) {
|
||||
< alloca risorsa >
|
||||
V (priv[p]);
|
||||
} else {
|
||||
< registro sospensione processo p>
|
||||
}
|
||||
V (mutex);
|
||||
P (priv[p]); // a seconda del valore di Cp, questa P e' sospensiva (!Cp) o vanificata dalla V (Cp)
|
||||
}
|
||||
```
|
||||
*nota: la risorsa in questo caso e' garantita dal processo che rilascia, perche' se Cp non e' verificata, questa procedura non alloca nulla*
|
||||
|
||||
#### Procedura di rilascio
|
||||
```
|
||||
public void rilascio (int j) {
|
||||
int p;
|
||||
P (mutex);
|
||||
< registro rilascio risorsa j >
|
||||
if ( < esiste almeno 1 processo sospeso > ) {
|
||||
< scelgo il processo p con massima priorita' >
|
||||
< alloco risorsa >
|
||||
< registro che p non e' sospeso >
|
||||
V (priv[p]);
|
||||
}
|
||||
V (mutex);
|
||||
}
|
||||
```
|
||||
#### Pro e contro della soluzione 2
|
||||
|
||||
* Un problema della soluzione 2 e' che e' limitato a 1 dato libero per volta (se i processi consumatori hanno bisogno di piu' di un "dato" (risorsa), non ho modo di sapere quanti ne sto rilasciando), mentre la soluzione 1 implementata con un *while* risolve questo problema.
|
||||
|
||||
* Siccome il processo rilasciante si occupa anche di allocare le risorse, non si puo' effettuare il passaggio di parametri. Questo caso e' ovviato dall'utilizzo di un array condiviso *allocazione* che fornisce l'indice della risorsa che e' stata allocata.
|
||||
|
||||
# Problema dell'accesso unico (senso unico alternato) - Readers & Writers
|
||||
|
||||
## Precedenza agli scrittori
|
||||
|
||||
L'implementazione con precedenza agli scrittori permette di bloccare i readers se un writer e' nella SC, ma anche se un writer e' in attesa.
|
||||
|
||||
## Soluzione bilanciata
|
||||
|
||||
Utilizzando dei semafori privati, e' possibile associare un semaforo privato a lettori e scrittori, ottenendo un accesso limitato degli scrittori alla SC, e quindi ottenendo una soluzione equilibrata.
|
||||
|
||||
|
3
anno2/YearI/MCAD/lesson8-24102017.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# L'esercizio: Il bar dello stadio
|
||||
|
||||
Questo esercizio sara' portato avanti con tutte le strutture di sincronizzazione trattate nel corso. *(vedi moodle)*
|
135
anno2/YearI/MCAD/lesson9-25102017.md
Normal file
|
@ -0,0 +1,135 @@
|
|||
# Monitor
|
||||
|
||||
Un **monitor** e' una combinazione di ADT (abstract data type, *tipo astratto*) e **mutua esclusione**.
|
||||
*(vedi slides per definizione)*
|
||||
* I monitor sono strutture dati che implementano operazioni mutualmente esclusive.
|
||||
|
||||
## Implementazione
|
||||
|
||||
```
|
||||
monitor stack {
|
||||
.
|
||||
.
|
||||
public push()
|
||||
public pop()
|
||||
.
|
||||
.etc
|
||||
}
|
||||
```
|
||||
Un monitor e' uno **stack**. *(slides per implementazione classica)*
|
||||
|
||||
### Esempio
|
||||
|
||||
* La definizione di monitor definisce un tipo *alpha*, secondo la definizione classica.
|
||||
* Il tipo definito puo' essere instanzializzato:
|
||||
```
|
||||
alpha x
|
||||
```
|
||||
* Le operazioni generiche e **pubbliche** definite dal monitor sono chiamate con **dot notation**:
|
||||
```
|
||||
x.op_i()
|
||||
```
|
||||
**Tutte le operazioni implementate dal monitor sono mutualmente esclusive**, permettendo quindi una semplificazione e sicurezza maggiore nella scrittura di programmi concorrenti.
|
||||
|
||||
**NOTA**: Il runtime support del linguaggio, che opera a livello kernel, usa comunque primitive semaforiche. L'utilizzo e' pero' astratto dal costrutto monitor.
|
||||
|
||||
## Variabili tipo condizione
|
||||
|
||||
Sono variabili che si possono usare solo all'interno del monitor. In genere, **ogni condition e' implementata tramite una coda**, di solito FIFO. Le procedure dei monitor agiscono sulle variabili condizione con le operazioni:
|
||||
|
||||
* wait(cond) <- sospende il processo in ogni caso e lo introduce nella coda individuata da cond
|
||||
* signal(cond) <- rende attivo un processo in attesa nella coda individuata da cond
|
||||
|
||||
**NOTA**: wait e signal **non sono** equivalenti a P,V per i semafori, in quanto gestiscono automaticamente le code e non permettono di testare condizioni (*if(cond)...* va utilizzato prima di wait).
|
||||
|
||||
*(slides per esempio)*
|
||||
Le variabili condizione possono essere utilizzate anche come semafori privati (N variabili condizione per N processi).
|
||||
|
||||
### Signal: realizzazione semantica
|
||||
|
||||
Signal non e' in grado di discriminare tra processi bloccati. Quindi bisogna discriminare, tra due processi, quale debba proseguire nella SC (processo *svegliante* o processo *svegliato*).
|
||||
|
||||
**Dipende dall'implementazione del singolo linguaggio**. Java, ad esempio, da la precedenza a un processo svegliante.
|
||||
|
||||
Questo implica l'utilizzo di un while loop per svegliare tutta la pool di processi in coda, che pero' non e' starvation free.
|
||||
#### Java: Signal and continue
|
||||
|
||||
Quando un processo viene svegliato da un altro, viene rimesso in coda, mentre il processo svegliante puo' continuare nella SC. Il processo svegliato quindi potrebbe ritrovarsi alla fine della coda.
|
||||
**Problema starvation svegliato**
|
||||
|
||||
#### Signal and wait
|
||||
|
||||
Opposto del signal and continue: il processo svegliato prende il posto dello svegliante, e lo svegliante va in fondo alla coda.
|
||||
**Problema starvation svegliante**
|
||||
|
||||
#### Signal and urgent wait
|
||||
|
||||
Signal and wait, ma il processo svegliante acquisisce la prima posizione nella coda (**urgent queue**). Questo e' l'approccio piu' *fair* rispetto a tutti i processi.
|
||||
|
||||
*(slides per esempio allocatore)*
|
||||
|
||||
**In una semantica SAUW, non ha senso utilizzare primitive signalAll**, in quanto violano il principio della mutua esclusione.
|
||||
|
||||
## Comparison: Monitor e Semafori
|
||||
|
||||
Qual'e' il piu' *potente* (inteso: che risolve la piu' vasta gamma di problemi) tra monitor e semafori?
|
||||
|
||||
* Sono **equivalenti**, in quanto con i semafori e' possibile implementare i monitor, e con i monitor e' possibile implementare i semafori. Non sono quindi piu' potenti dei semafori, sono solo un'astrazione di essi.
|
||||
|
||||
*(vedi slides per dimostrazione di realizzazione)*
|
||||
|
||||
## Operazioni aggiuntive su variabili condizione
|
||||
|
||||
* **wait(cond,p)**: wait con indicazione della priorita', che e' data come numerica, in ordine di solito decrescente. (priorita' 0 = massima). Il linguaggio implementa quindi una *priority queue*.
|
||||
|
||||
* **empty(cond)**: true se la coda e' vuota, false se ci sono processi in attesa
|
||||
|
||||
* **signalAll(cond)**: rende attivi tutti i processi in coda, ma si puo' utilizzare **solo con signal and continue**.
|
||||
|
||||
## Esempi
|
||||
|
||||
### Shortest-job-next: allocazione di risorse
|
||||
|
||||
```
|
||||
monitor allocatore {
|
||||
bool occupata = false;
|
||||
condition non_occ;
|
||||
|
||||
public void richiesta (int tempo) {
|
||||
if (occupata) {
|
||||
wait (non_occ, tempo);
|
||||
occupata = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
*(altri esempi su slides, disco a teste mobili, lettori / scrittori con monitor)*
|
||||
|
||||
#### Monitor: Lettori / scrittori - Prova di correttezza
|
||||
|
||||
La prova di correttezza di un monitor si ottiene tramite gli **invarianti di monitor**, dimostrate valide per induzione:
|
||||
|
||||
* sono vere al momento dell'inizializzazione del monitor
|
||||
* sono vere all'uscita di esecuzione della chiamata al monitor
|
||||
|
||||
L'invariante da dimostrare e' il seguente:
|
||||
siano:
|
||||
* R - numero di processi in lettura
|
||||
* W - numero di processi in scrittura
|
||||
|
||||
```
|
||||
(R>0 => W=0) and (W <= 1) and (W = 1 => R = 0)
|
||||
```
|
||||
* Facilmente dimostrabile e' il seguente gruppo di invarianti:
|
||||
```
|
||||
R = num_lett
|
||||
W = num_scritt
|
||||
R >= 0
|
||||
W >= 0
|
||||
```
|
||||
##### Dimostrazione
|
||||
|
||||
* All'inizio, l'invariante in tesi e' chiaramente vero.
|
||||
* Supposto vero all'inizio di ciascuna delle 4 procedure di monitor, e' vero anche alla fine.
|
||||
|
||||
*(vedi slides per casi specifici)*
|
104
anno2/YearI/MCAD/seminario/recap.md
Normal file
|
@ -0,0 +1,104 @@
|
|||
# Problema del consenso con Bizantine Failures
|
||||
|
||||
Considerando il modello sincrono, il modello a fallimenti bizantini vuole rappresentare una categoria di malfunzionamenti in cui i processi possono esibire un comportamento imprevedibile, in contrapposizione con il modello a stopping failures che vuole rappresentare processi che falliscono fermandosi senza warning.
|
||||
|
||||
## Il problema del consenso
|
||||
|
||||
*(agreement problem)*
|
||||
I processi sono **inizializzati con inputs individuali da un set di valori V**. Tutti i processi che eseguono correttamente **devono produrre un output appartenente allo stesso set di valori V**.
|
||||
L'output e' soggetto a un semplice consenso e a condizioni di validita'. Per la validita', si assume che se ogni processo inizia con lo stesso valore `v`, allora l'unico valore permesso e' `v`.
|
||||
Il problema del consenso deriva dallo sviluppo di on-board aircraft control systems, in cui un numero di processi, ognuno con il proprio altimetro indipendente, devono raggiungere un accordo (*consenso*) riguardante l'altitudine dell'aereo. Qualche processo potrebbe essere malfunzionante.
|
||||
|
||||
Il numero di *failures* e' considerato limitato in partenza, da un numero `f` fissato. Questo permette di evitare l'analisi probabilistica del numero di fallimenti, per semplicita'. Si nota pero' che questo e' poco pratico nelle applicazioni reali, dove i failures sono solitamente indipendenti (o correlati positivamente), mentre fissando il numero massimo in partenza risultano correlati negativamente.
|
||||
|
||||
*Nota: il coefficiente di correlazione ha range [-1,1], per cui:*
|
||||
|
||||
* *>0 implica correlazione positiva, quindi le variabili crescono e decrescono assieme (se una cresce, anche l'altra cresce, e viceversa).
|
||||
* <0 implica correlazione negativa, quindi le variablil cresconon e decrescono in maniera opposta (se una cresce, l'altra decresce, e viceversa).*
|
||||
|
||||
#### Ridondanza
|
||||
|
||||
Gli **algoritmi di consenso Bizantini** sfruttano spesso la ridondanza di processi che compiono uguali operazioni / computazioni per garantire robustezza contro i fallimenti bizantini.
|
||||
|
||||
### Statement del problema
|
||||
|
||||
Si assume che il network e' un **grafo non direzionale connesso** con processi 1...n, in cui ogni processo **conosce l'intero grafo**. Ogni processo inizia con un input da un set di valori fissato V, assumendo che per ogni processo c'e' **esattamente uno stato iniziale contenente ogni valore di input**. L'obbiettivo dei processi e' di restituire in output decisioni dallo stesso set di valori V, settando dei componenti speciali detti **stati di decisione** a valori in V.
|
||||
Il numero di processi che puo' fallire e' limitato a `f`, tutti i link sono invece *reliable*, ossia non possono fallire. Di conseguenza, tutti i messaggi inviati sono consegnati.
|
||||
In questa situazione, i tipi di malfunzionamento considerati (stopping failures, fallimenti bizantini) sono gli unici possibili.
|
||||
|
||||
#### Stopping failures model - condizioni di correttezza
|
||||
|
||||
In presenza di stopping failures, un processo puo' fermarsi senza preavviso, anche nel mezzo di un invio di messaggi. Le condizioni di correttezza del problema del consenso sono quindi:
|
||||
|
||||
* **CONSENSO**: Nessuna coppia di processi decide valori differenti.
|
||||
* **VALIDITA'**: Se tutti i processi partono con lo stesso valore iniziale `v` appartenente a V, allora `v` e' l'unico valore decisione possibile.
|
||||
* **TERMINATION**: Tutti i processi funzionanti correttamente, alla fine, decidono (settano lo stato di decisione).
|
||||
|
||||
### Bizantine failures model - condizioni di correttezza
|
||||
|
||||
In presenza di fallimenti bizantini, un processo potrebbe fallire non solo fermandosi, ma esibendo un comportamento indeterminato. Questo vuol dire che potrebbe iniziare in uno stato arbitrario, non necessariamente uno dei suoi stati iniziali, potrebbe mandare **messaggi arbitrari**, ed eseguire **transizioni di stato arbitrarie**. L'unica limitazione e' che un processo puo' influenzare solo le variabili sulle quali ha controllo (il suo stato e i messaggi di output che manda).
|
||||
|
||||
* **CONSENSO**: Nessuna coppia di processi **funzionanti correttamente** decide valori differenti.
|
||||
* **VALIDITA'**: Se tutti i processi **funzionanti correttamente** partono con lo stesso valore iniziale `v` appartenente a V, allora `v` e' l'unico valore decisione possibile.
|
||||
* **TERMINATION**: Tutti i processi funzionanti correttamente, alla fine, decidono (settano lo stato di decisione).
|
||||
|
||||
### Relazione tra stopping failures e bizantine failures
|
||||
|
||||
Si nota come **un algoritmo che risolve il problema dei fallimenti bizantini solitamente NON risolve gli stopping failures**, mentre **il contrario di solito e' vero**. Questo perche' negli stopping failures e' richiesto che tutti i processi, anche quelli malfunzionanti, producano una decisione corretta.
|
||||
|
||||
#### Nota: misurazioni complesse
|
||||
|
||||
Per misurare la *time complexity* si contano tutti i cicli finche' ogni processo funzionante correttamente decide. Per misurare la *communication complexity*, si contano sia il numero di messaggi sia il numero di bit di comunicazione. **Per i processi bizantini, non c'e' modo di stabilire un boundary sulla comunicazione mandata da un processo malfunzionante.**
|
||||
|
||||
## Algoritmi per Stopping failures
|
||||
|
||||
### 6.2.1 - Basic algorithm (FloodSet)
|
||||
|
||||
Si basa su un funzionamento semplice: **i processi propagano tutti i valori di V che hanno mai visto / considerato, e utilizzano una semplice regola di decisione alla fine.**
|
||||
|
||||
*(vedi libro per dimostrazione di correttezza, lemmi, pseudocodice)*
|
||||
|
||||
### 6.2.2 - Ridurre la comunicazione del FloodSet
|
||||
|
||||
Uno dei problemi dell'algoritmo FloodSet e' che il numero di messaggi che richiede e' abbastanza alto, `O((f+1)*n^2)`, come anche il numero di bits, `O((f+1)*n^3)`.
|
||||
|
||||
Il numero di messaggi puo' essere ridotto a `O(2*n^2)` utilizzando l'idea seguente.
|
||||
Essendo che, alla fine dell'esecuzione dell'algoritmo, il processo `i` necessita di conoscere solo gli elementi esatti del suo set `W[i]` se `|W[i]| = 1`, altrimenti necessita di sapere solo che `|W[i]| >= 2`. Per questo motivo, ogni processo puo' mandare in broadcast **solo i primi due valori che vede**, invece che tutti.
|
||||
|
||||
### 6.2.3 - Algoritmi di raccolta delle informazioni esponenziale (EIG)
|
||||
|
||||
*(Exponential information Gathering algorithms)*
|
||||
La strategia EIG si struttura come segue:
|
||||
|
||||
* i processi mandano e ricevono valori iniziali per vari cicli, salvando i valori ricevuti in una data structure chiamata **EIG tree**.
|
||||
* alla fine, i processi utilizzano una regola di decisione comune basata sui valori registrati nei propri *trees*.
|
||||
|
||||
Questi algoritmi sono di solito molto costosi (*overkill*) per la risoluzione di stopping failures, ma la struttura dell'EIG tree e' utilizzata anche nella risoluzione di casi di fallimento bizantino.
|
||||
|
||||
### 6.2.4 - Consenso bizantino con autenticazione
|
||||
|
||||
Gli algoritmi EIG descritti in [questa sezione](#6.2.3) sono pensati per tollerare stopping failures, ma alcuni possono tollerare anche tipi peggiori di fallimenti, anche se non sono adeguati per risolvere il modello completo di fallimenti bizantini. Possono pero' risolvere un *subset* del modello dei fallimenti bizantini, quello in cui i processi hanno la capacita' di **autenticare le proprie comunicazioni**, utilizzando **firme digitali** (*digital signatures*).
|
||||
|
||||
## Algoritmi per Fallimenti Bizantini
|
||||
|
||||
Una proprieta' comune di tutti gli algoritmi presentati in seguito e' che il numero di processi che usano e' **piu' di 3 volte il valore di failures**, quindi `n > 3*f`.
|
||||
|
||||
### 6.3.1 - Esempio (vedi libro)
|
||||
|
||||
### 6.3.2 - Algoritmo EIG per fallimenti bizantini
|
||||
|
||||
L'algoritmo presentato e' chiamato **EIG-biz**. L'algoritmo presuppone che il **numero di processi e' grande rispetto al numero di fallimenti**, con la regola `n > 3*f`. Utilizza la stessa struttura dati degli algoritmi EIG presentati in 6.2, nonche' la stessa strategia di propagazione. L'unica differenza e' che un processo che riceve un messaggio malformato **corregge l'informazione di modo che sia accettabile**. Un'altra grande differenza e' la **decision rule**, infatti i processi non possono piu' fare affidamento sui valori che compaiono sul suo *tree*, ma deve mascherare i valori che arrivano con messaggi malformati.
|
||||
|
||||
### 6.3.3 - Consenso Bizantino generico - utilizzo di Consenso Bizantino Binario
|
||||
|
||||
Si mostra un algoritmo che risolve il consenso bizantino per input in `{0,1}`, come una subroutine per risolvere consenso bizantino generico. L'overhead e' di 2 cicli aggiuntivi, `2*n^2` messaggi aggiuntivi e `O(n^2*b)` bits.
|
||||
|
||||
### 6.3.4 - Ridurre la comunicazione
|
||||
|
||||
Nonostante l'algoritmo per la risoluzione di consenso bizantino binario possa essere usato per ridurre la comunication complexiti nel Bizantine Agreement, il suo costo e' comunque esponenziale nel numero di fallimenti. In generale **gli algoritmi polinomiali nel numero di fallimenti sono molto piu' difficili da ottenere nel modello di fallimenti bizantini che nel modello di stopping failures.** L'algoritmo presentato usa una tecnica detta di **consistent broadcast** per tutte le sue comunicazioni.
|
||||
Questo meccanismo e' un modo di assicurare un determinato livello di **coerenza** tra i messaggi ricevuti dai diversi processi.
|
||||
Si basa sul fatto che un processo puo' fare *broadcast* di un messaggio nella forma (m,i,r) al round `r`. I processi che ricevono possono poi **accettare** il messaggio a un round successivo qualunque. Il meccanismo consiste nel:
|
||||
|
||||
* Se un processo funzionante fa broadcast di un messaggio, allora al round successivo il messaggio e' accettato da tutti i processi funzionanti correttamente.
|
||||
* Se un processo funzionante **non fa broadcast** di un messaggio, allora il messaggio non e' mai accettato dai processi funzionanti correttamente.
|
||||
* Se un messaggio e' accettato da qualunque processo funzionante correttamente, allora nel round successivo e' accettato da tutti i processi funzionanti correttamente.
|