title: race conditions with php sessions and ajax request
body: <p>I have a shopping cart I built myself that uses session variables to maintain the state of cart across requests. I have an increment and decrement button that allows users to increase or decrease the quantity of a product in their cart. this happens via an Ajax request. The cart class operates by restoring the cart out of the session when constructed, and saving the cart back to the session when destructed. </p>
  
  <pre><code>&lt;?php
  
  class Cart {
  
      /**
       * constructor
       */
      public function __construct(){
  
          //restore cart
          $this-&gt;restore();
      }
  
      /**
       * destructor
       */
      public function __destruct(){
  
          //save cart
          $this-&gt;save();
      }
  
      /**
       * restore
       */
      public function restore(){
  
          //retrieve session info
          if(Session::has('cart')){
  
              //get cart
              $session = Session::get('cart');
  
              //assign session info
              $this-&gt;data = ($session['data']);
              $this-&gt;rates = $session['rates'];
              $this-&gt;lines = $session['lines'];
          }
      }
  
      /**
       * save
       */
      public function save(){
          Session::put('cart', $this-&gt;forSession());
      }
  </code></pre>
  
  <p>the problem I'm running into is a race condition with multiple Ajax requests. the user can hit the button many times, sending multiple Ajax requests. each request is therefore pulling the current state of the session, doing the operation, and then saving it. the problem is the previous transaction is not necessarily completed and saved when it <code>restore</code>s the cart. my first fix was to make any subsequent Ajax requests cancel the previous one, to both cut down on unnecessary (immediately overwritten) requests, and also to help avoid this race condition. while it seemed to help, it still acts quirky. So my next thought was to attack it at the source, namely the cart class itself. my idea was to implement some type of 'locking' to prevent the cart from being accessed until a previous operation was completed. The idea looked something like this.</p>
  
  <pre><code>&lt;?php
  
  /**
   * is cart locked
   */
  public function isCartLocked(){
  
      if(Session::get('cartLock') === 1){
          sleep(1);
          $this-&gt;isCartLocked();
      }
  }
  
  public function restore(){
  
      Session::put('cartLock', 0);
      //check if cart is locked
      $this-&gt;isCartLocked();
  
      //lock cart
      Session::put('cartLock', 1);
  
      ...
  }
  
  public function save(){
  
      //unlock the cart
      Session::put('cartLock', 0);
  
      ...
  }
  </code></pre>
  
  <p>now the first question is, SHOULD I be doing something like this, with the locking? And then, if so, is this a decent way to handle it?</p>
  
  <p>After my first attempt at it, the problem I seem to be running into is the destructor is not necessarily called all the time, which is causing my cart to stay locked, and eventually causing a timeout error.</p>
  
  <p>Thanks for any help!</p>