Skip to content

[πŸ› Bug]: Program exit is slow only on WindowsΒ #13850

@marvelm

Description

@marvelm

What happened?

Hello,

Apologies for the vague title, but it completely summarizes what we've been experiencing.

When running this code with a profiler, we discovered that send_remote_shutdown_command is taking >30 seconds to finish.

To get to the bottom of the issue, we monkey-patched this function. This gave us an unexpected output. .quit() was only called 3 times, but the output shows that send_remote_shutdown_command was called 6 times. I think it's because __exit__ is being called by the with statement.

def __exit__(
self,
exc_type: typing.Optional[typing.Type[BaseException]],
exc: typing.Optional[BaseException],
traceback: typing.Optional[types.TracebackType],
):
self.quit()

On macOS and Linux, calling quit() twice is quick because the default TCP socket timeout is low. However on Windows, the default timeout is long.

request.urlopen(f"{self.service_url}/shutdown")

Solution

We're working around this issue on Windows by monkey-patching the function and setting the timeout to a small value.

from selenium.webdriver.common.service import Service
from urllib import request
from urllib.error import URLError


def send_remote_shutdown_command(self) -> None:
    try:
        request.urlopen(f"{self.service_url}/shutdown", timeout=0.1)
    except URLError:
        return


Service.send_remote_shutdown_command = send_remote_shutdown_command

How can we reproduce the issue?

import threading
from concurrent.futures import ThreadPoolExecutor
from selenium.webdriver import Chrome
from selenium.webdriver.common.service import Service

fn_copy = Service.send_remote_shutdown_command

count = 0
mut = threading.Lock()


def send_remote_shutdown_command(self: Service) -> None:
    global count
    with mut:
        count += 1
        print(f"CALLED: {count} times")
    fn_copy(self)


Service.send_remote_shutdown_command = send_remote_shutdown_command

class MyObject:
    def __init__(self, id):
        self.id = id


objects = [MyObject(i) for i in range(3)]


def run(obj):
        chrome = Chrome()
        obj.browser = chrome
        chrome.get("https://www.google.com")
        print("quit", obj.id)
        chrome.quit()

with ThreadPoolExecutor(max_workers=1) as executor:
    for result in executor.map(run, objects):
        print("result")

print("script complete")

Relevant log output

quit 0
CALLED: 1 times
CALLED: 2 times
quit 1
CALLED: 3 times
CALLED: 4 times
quit 2
CALLED: 5 times
CALLED: 6 times
script complete

Operating System

Windows 10

Selenium version

4.19.0

What are the browser(s) and version(s) where you see this issue?

124.0.6367.60

What are the browser driver(s) and version(s) where you see this issue?

ChromeDriver 123.0.6312.122

Are you using Selenium Grid?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-pyPython BindingsI-defectSomething is not working as intendedJ-staleApplied to issues that become stale, and eventually closed.R-help wantedIssues looking for contributions

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions