Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serve will do its job even if stop was called before #598

Open
1 of 3 tasks
kasium opened this issue Jan 27, 2023 · 0 comments
Open
1 of 3 tasks

Serve will do its job even if stop was called before #598

kasium opened this issue Jan 27, 2023 · 0 comments
Labels
bug Something is broken triage

Comments

@kasium
Copy link
Contributor

kasium commented Jan 27, 2023

❓ I'm submitting a ...

  • 🐞 bug report
  • 🐣 feature request
  • ❓ question about the decisions made in the repository

🐞 Describe the bug. What is the current behavior?
If stopped is called on a preparing server, the server won't stop if it's reaches the serve state.

❓ What is the motivation / use case for changing the behavior?
I need a cheroot server which starts in another thread. Sometime it can happen that a server hangs during the prepare, e.g. due to a socket error. Therefore the "parent" thread will then stop the server and retry later.

πŸ’‘ To Reproduce
Of course this will not happen in the real world like this. I used events to showcase the issue in a reproducible way:

from flask import Flask
from cheroot.wsgi import Server
from threading import Thread, Event

class WaitServer(Server):
    def bind_unix_socket(self, *args, **kwargs):
        print("Set bind_called_event")
        bind_called_event.set()
        print("Wait for stopped_event")
        stopped_event.wait()
        print("Exec bind_unix_socket")
        super().bind_unix_socket(*args, **kwargs)

def _run():
    print("[T] Start server")
    server.start()

app = Flask(__name__)
server = WaitServer(bind_addr="localhost", wsgi_app=app, numthreads=1)
thread = Thread(target=_run)
bind_called_event = Event()
stopped_event = Event()

print("Starting thread")
thread.start()
print("Wait for bind_called_event")
bind_called_event.wait()

server.stop()
print("Set stopped_event")
stopped_event.set()
print("Wait for thread")
thread.join()
print("Stopped everything")

πŸ’‘ Expected behavior
When stopped is called, the server stops as soon as possible

πŸ“‹ Details
The issue can be also be fixed, by using interrupt instead of stop.

from flask import Flask
from cheroot.wsgi import Server
from threading import Thread, Event

class WaitServer(Server):
    def bind_unix_socket(self, *args, **kwargs):
        print("Set bind_called_event")
        bind_called_event.set()
        print("Wait for stopped_event")
        stopped_event.wait()
        print("Exec bind_unix_socket")
        super().bind_unix_socket(*args, **kwargs)

def _run():
    print("[T] Start server")
    try:
        server.start()
    except Exception:
        pass


app = Flask(__name__)
server = WaitServer(bind_addr="localhost", wsgi_app=app, numthreads=1)
thread = Thread(target=_run)
bind_called_event = Event()
stopped_event = Event()

print("Starting thread")
thread.start()
print("Wait for bind_called_event")
bind_called_event.wait()

server.interrupt = Exception("stopping")
print("Set stopped_event")
stopped_event.set()
print("Wait for thread")
thread.join()
server.stop()
print("Stopped everything")

However, the code get's more complicated:

  • try/except when usign start
  • an exception needs to be used in the first place
  • server.stop needs to be called again to shutdown workers

If the last server.stop is not used, the program is stucked again. E.g. with py-spy:

Process 12334: python bar.py
Python v3.7.1 (/usr/bin/python3.7)

Thread 12334 (idle): "MainThread"
    _wait_for_tstate_lock (threading.py:1048)
    join (threading.py:1032)
    _shutdown (threading.py:1273)
Thread 12346 (idle): "CP Server Thread-2"
    wait (threading.py:296)
    get (queue.py:170)
    run (cheroot/workers/threadpool.py:110)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)

πŸ“‹ Environment

  • Cheroot version: 9.0.0
  • CherryPy version: n/a
  • Python version: 3.7.1
  • OS: Linux
  • Browser: n/a
@kasium kasium added bug Something is broken triage labels Jan 27, 2023
@kasium kasium changed the title Start/Stop is not thread-safe Serve will do its job even if stop was called before Jan 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something is broken triage
Projects
None yet
Development

No branches or pull requests

1 participant