RenderJS Home RenderJS

    How To Prevent Concurrency Execution With A Mutex

    • Last Update:2020-05-14
    • Version:001
    • Language:en

    In order to prevent concurrent access to some resources, the renderJS methods can be protected by a mutex.
    A protected method is executed only when the previous call is resolved/rejected.

    One mutex can also be uses to protect the different methods.

    To use a mutex, pass an optional "mutex" parameter to the declareMethod like:

    .declareMethod("methodName", function () {...}, {mutex: "mutex_name"})

    Here is a full example:

    Create one child gadget

    HTML

    <html>
      <head>
        <script src="rsvp.js"></script>
        <script src="renderjs.js"></script>
        <script src="howto_mutex_child.js"></script>
      </head>
      <body>
      </body>
    </html>
    

    JS

    (function (rJS, RSVP, window) {
      "use strict";
    
      function resetCounter() {
        this.counter = 0;
      }
    
      function increaseCounterAndWaitAndReturn() {
        var gadget = this;
        gadget.counter += 1;
        return new RSVP.Queue(RSVP.delay(50))
          .push(function () {
            return gadget.counter;
          });
      }
    
      rJS(window)
        .ready(resetCounter)
        .declareMethod("executeNotProtectedMethod", increaseCounterAndWaitAndReturn)
        .declareMethod("executeFirstNonConcurrentMethod", increaseCounterAndWaitAndReturn, {mutex: "first_mutex"})
        .declareMethod("executeSecondNonConcurrentMethod", increaseCounterAndWaitAndReturn, {mutex: "first_mutex"})
        .declareMethod("resetCounter", resetCounter);
    }(rJS, RSVP, window));
    
    

    Create the parent gadget

    HTML

    <html>
      <head>
        <script src="rsvp.js"></script>
        <script src="renderjs.js"></script>
        <script src="howto_mutex_parent.js"></script>
      </head>
      <body>
        <pre></pre>
      </body>
    </html>
    

    JS

    (function (rJS, RSVP, window) {
      "use strict";
    
      function log(gadget, text, expected_value, value) {
        gadget.element.querySelector("pre").textContent += text + " - expected: " + expected_value + " got: " + value + "\n";
      }
    
      rJS(window)
        .declareService(function () {
          var parent_gadget = this,
            child_gadget;
          return parent_gadget.declareGadget("howto_mutex_child.html", {scope: "child_scope"})
            .push(function (result) {
              child_gadget = result;
    
              // If not protected with a mutex, method are executed concurrently
              return RSVP.all([
                child_gadget.executeNotProtectedMethod(),
                child_gadget.executeNotProtectedMethod()
              ]);
            })
            .push(function (result_list) {
              log(parent_gadget, "First executeNotProtectedMethod", 2, result_list[0]);
              log(parent_gadget, "Second executeNotProtectedMethod", 2, result_list[1]);
    
              // One method is protected by a mutex
              return RSVP.all([
                child_gadget.resetCounter(),
                child_gadget.executeFirstNonConcurrentMethod(),
                child_gadget.executeFirstNonConcurrentMethod()
              ]);
            })
            .push(function (result_list) {
              log(parent_gadget, "First executeFirstNonConcurrentMethod", 1, result_list[1]);
              log(parent_gadget, "Second executeFirstNonConcurrentMethod", 2, result_list[2]);
    
              // Two different methods can use the same mutex
              return RSVP.all([
                child_gadget.resetCounter(),
                child_gadget.executeFirstNonConcurrentMethod(),
                child_gadget.executeSecondNonConcurrentMethod()
              ]);
            })
            .push(function (result_list) {
              log(parent_gadget, "First executeFirstNonConcurrentMethod", 1, result_list[1]);
              log(parent_gadget, "Second executeSecondNonConcurrentMethod", 2, result_list[2]);
            });
        });
    }(rJS, RSVP, window));