title: How to use locks with server-side objects in flask/socketio application?
body: <p>I have a server which hosts a two-player game online. It is a turn-based game. The server maintains a list of all the ongoing games (each game is represented as an object of my class <code>Game</code>). The <code>Game</code> class manages each game, including whose turn it is. Players communicate with each other by calling sockets on the server (e.g., a <code>Movement</code> socket), which then grab the game object associated with the player's game, and operate on that game object.</p>
  <p>One of the players is actually automated, and running as a separate process on the server side (though it communicates the same way a browser-based player would). However, this means the protocol for getting this player to send <code>Movement</code> is: (1) server sends a message to the automated player process requesting an action, (2) automated player responds with an action (potentially ~1 second later) to the <code>Movement</code> socket, and then (3) the server processes this action (e.g., forwards it to the other player).</p>
  <p>The game is turn-based, but I need to allow players to interrupt each other's turn. Specifically: pressing a special key should end the other player's turn. The interrupting player's game triggers the <code>Cancel</code> socket on the server, which grabs the game object, and then should switch turn control to the other player (and notify the other player their turn has ended).</p>
  <p>However, I don't want the server to process a <code>Cancel</code> while it's waiting for a <code>Movement</code> from the automated player. This can break the synchronization between the two players and the server. I tried addressing this by adding a <code>threading.Lock</code> attribute as a member of the <code>Game</code> class. So my <code>Game</code> class looks like this:</p>
  <pre><code>import threading
  class Game:
    def __init__(self):
      self.lock = threading.Lock()
  
    def process_movement(self):
      # Do some stuff here, like forward the message to the other player
      pass
  
    def process_cancel(self):
      # Do some stuff here, like end the current player's turn, and send the message to the other player
      pass
  
    def request_movement(self):
      # Acquire the lock here...
      self.lock.acquire()
      emit('requestAction', room=self.automated_player_room())
  
    def is_automated_players_turn(self):
      # Returns bool according to if it's the automated player's turn
      pass
  
  </code></pre>
  <p>And every time either of those two sockets are called, they acquire the game's lock, operate on the object, and then release the lock. For example, the <code>Cancel</code> socket:</p>
  <pre><code>@socketio.on('Cancel')
  def cancel():
    game_obj: Game = get_game()
    game_obj.lock.acquire()
    game_obj.process_cancel()
    game_obj.lock.release()
  </code></pre>
  <p>and for the <code>Movement</code> socket:</p>
  <pre><code>@socketio.on('Movement')
  def movement():
    game_obj: Game = get_game()
    game_obj.process_movement()
  
    # Release the lock here
    game_obj.lock.release()
    if game_obj.is_automated_player_turn():
      game.request_action()
  </code></pre>
  <p>However, I am noticing that for some games, the lock is violated: even though the <code>Movement</code> socket hasn't yet been called, so the lock should be locked, <code>Cancel</code> can still proceed to call <code>process_cancel()</code>. Unfortunately, I actually can't replicate this error myself; only other people playing the game have been able to replicate it, and I can't figure out the conditions under which it happens.</p>
  <p>Am I using locks correctly? Why would the lock be violated if this is my setup?</p>
  <p>Thanks!</p>
  <p>Edit: I heard that python doesn't seem to guarantee much about threads which acquire but don't release locks. Now looking for suggestions on a better data structure I should use.</p>