miguelgrinberg / simple-websocket

Simple WebSocket server and client for Python.
MIT License
78 stars 17 forks source link

High CPU usage after chromium client disconnect #4

Closed bryanniwa closed 2 years ago

bryanniwa commented 2 years ago

Environment

Flask server using Flask-Socketio and simple-websocket. Client connects using Chromium. Issue is not present with Chrome. Chromium client may be on the same machine as the server or another machine. Tested using Ubuntu 18 on ARM 32bit and WSL2 on Windows 10 64 bit. Tested using python 3.6, 3.8 and 3.9

Steps to reproduce

I have managed to reproduce the issue using the following minimal server.

from flask import Flask
from flask_socketio import SocketIO

app = Flask(__name__)
socketio = SocketIO(app, cors_allowed_origins="*", async_mode="threading")

@socketio.on("connect")
def connect():
    print("on connect")

if __name__ == "__main__":
    socketio.run(app, port=5050, host='0.0.0.0')
  1. run the server using `python main.py'
  2. Using htop or another similar tool, observe that CPU usage is near 0%
  3. Connect to the server
    • In my actual application I have HTML pages being served but I was able to reproduce the issue using hoppscotch.io as well
  4. Force close Chromium either through pkill chromium or using task manager on windows
  5. Using htop, or a similar tool, notice that CPU usage of the server jumps to 100%

More information

I used the program py-spy to profile the running server and traced the high CPU usage to the _thread function in ws.py. As soon as the client is closed, in_data = self.sock.recv(self.receive_bytes) on line 111 receives empty byte objects as fast as the while loop can process them MicrosoftTeams-image

I managed to fix the problem by adding the following check after the try-except block however I'm not sure if it's the best solution.

if len(in_data) == 0:
    self.connected = False
    self.event.set()
    break

Thanks for all your work

miguelgrinberg commented 2 years ago

Thank you so much for providing all the details of your investigation. This saves me a ton of time. :)

Yes, this is an oversight on my part, a socket that returns no data on the recv function should be assumed to have been closed/abandoned on the other side.