Sistemas y Tecnologías Web: Servidor

Master de II. ULL. 1er cuatrimestre. 2020/2021


Organization ULL-MII-SYTWS-2021   Classroom ULL-MII-SYTWS-2021   Campus Virtual SYTWS   Chat Chat   Profesor Casiano

Table of Contents

Race Conditions

Loading an image with some delay

Consider this file index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Asynchronous Image Loading</title>
</head>
<body>
  <div id="holder-div"></div>

  <script type="text/javascript">
    let image = new Image(100),
        url = "https://elementscpa.com/wp-content/uploads/2017/08/elementscpa-business-infinity-loop-tal-e1504182065499.png", 
        //url = "infinity-loop.png", 
        container = document.getElementById("holder-div");

    image.src = url; // I suppose the "load" process starts here

    let waitFor = 0;
    //let waitFor = 1000;
    setTimeout(function(){
      // The onload event occurs when an object has been loaded
      // We only append it to the container when the load has finished
      // If 1000 the handler is inserted in the event queue too late
      // If an EventListener is added to an EventTarget while it is 
      // processing an event, that event does not trigger the listener.
      image.addEventListener("load", function() {
        console.trace();
        container.appendChild(image)
      });
    }, waitFor);
  </script>
  <a href="http://www.infoq.com/presentations/javascript-concurrency-parallelism">Concurrency and Parallel Computing in JavaScript (Recorded at: StrangeLoop) by Stephan Herhut on Mar 05, 2014 </a>
</body>
</html>

Experiment

Copy and open this file index.html with your browser. Can you see the infinite loop image?

Now comment the line where waitFor is initialized and uncomment the other:

1
let waitFor = 0;

What do you think it will happen? Can you explain it?

Where is it:

1
2
➜  race-condition git:(curso2021) ✗ ls ~/campus-virtual/2021/sytws2021/apuntes/tema2-async/event-loop/exercises/race-condition
index.html          infinity-loop.png   instructions.md     not-race-example.js race-example.js

References

Manually Interleaving Promises

This other example (using promises instead of callbacks) is taken from this blog:

Promises are a (relatively) new way to handle asynchronous programming in JS. For an introduction to Promises see this section:

File race-example.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// https://medium.com/@ubershmekel/yes-there-are-race-conditions-in-javascript-ba044571a914
// An example race condition in JavaScript
// When you run this script using Node or in a browser, it
// does not print "Ended with 0", but a random number.
// Even though the functions running
// simply loop 100 iterations of adding and subtracting.
// The reason the end result is random is because the
// sleeps are of random duration and the time between the read
// of the variable causes the eventual write to be incorrect
// when `adder` and `subber` interleave.
// This problem is similar to:
// https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use

let number = 0;
const times = 100;

function sleep() { 
  return new Promise(
    (resolve) => setTimeout(resolve, Math.random() * 5));
}

async function adder() {
  for (let i = 0; i < times; i++) {
    await sleep();

      let read = number; 
      read = read + 1;

    await sleep(); // This task is interrupted here giving opportunity for 'subber' to take the processor

    number = read; // But there is a chance 'read' is obsolete. 
                   // It is a sort of "manual" co-routine race cndition: not atomic anymore
  }
}

async function subber() {
  for (let i = 0; i < times; i++) {
    await sleep();
      let read = number;
      read = read - 1;
    await sleep();

    number = read;
  }
}

async function main() {
  console.log("Started with", number);

  await Promise.all([
    adder(),
    subber(),
  ]);
  /*
  await adder().then(subber)
  */

  console.log("Ended with", number);
}

main()
  .then(() => console.log("All done"))
  .catch((err) => console.error(err));

An example of race condition in JavaScript. When you run this script using Node or in a browser, it does not print “Ended with 0”, but a random number.

1
2
3
4
5
6
7
➜  race-condition git:(curso2021) ✗ node race-example.js 
Started with 0
Ended with 3
All done
➜  race-condition git:(curso2021) ✗ node race-example.js
Started with 0
Ended with 20

Even though the functions running simply loop 100 iterations of adding and subtracting.

The reason the end result is random is because the sleeps are of random duration and the time between the read of the variable causes the eventual write to be incorrect when adder and subber interleave.

This problem is similar to:

Time-of-check to time-of-use

Comment with GitHub Utterances

Comment with Disqus

thread de discusion