Export to GitHub

chromium-os - issue #13043

chromeos-chrome frequently blocks for 60 seconds on single-threaded HTTP server


Posted on Mar 11, 2011 by Massive Giraffe

chromium os version 0.11.250 (own build from a few days ago) chromium version 11.0.697.0 (own build from today)

When running with the python SimpleHTTPServer, chromeos-chrome blocks for about 60s on many requests. When reloading a page, it alternates between loading it immediately and waiting 60s.

Below is the strace output from the python server. It shows that the browser satisfies one request, then it receives another one, but after the select() and accept(), it gets stuck on the recv() waiting for the HTTP GET. This works fine on linux chrome.

accept(3, {sa_family=AF_INET, sin_port=htons(42758), sin_addr=inet_addr("127.0.0.1")}, [16]) = 4 recv(4, "GET /platform_BrowserLoad.py HTT"..., 8192, 0) = 499 getcwd("/usr/local/autotest/tests/platform_BrowserLoad", 1024) = 47 stat64("/usr/local/autotest/tests/platform_BrowserLoad/platform_BrowserLoad.py", {st_mode=S_IFREG|0755, st_size=2372, ...}) = 0 open("/usr/local/autotest/tests/platform_BrowserLoad/platform_BrowserLoad.py", O_RDONLY|O_LARGEFILE) = 5 fstat64(5, {st_mode=S_IFREG|0755, st_size=2372, ...}) = 0 open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 6 fstat64(6, {st_mode=S_IFREG|0644, st_size=1061, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x77343000 read(6, "# /etc/hosts: Local Host Databas"..., 4096) = 1061 close(6) = 0 munmap(0x77343000, 4096) = 0 gettimeofday({1299885101, 789546}, NULL) = 0 stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2819, ...}) = 0 write(2, "localhost - - [11/Mar/2011 15:11"..., 83localhost - - [11/Mar/2011 15:11:41] "GET /platform_BrowserLoad.py HTTP/1.1" 200 - ) = 83 send(4, "HTTP/1.0 200 OK\r\n", 17, 0) = 17 send(4, "Server: SimpleHTTP/0.6 Python/2."..., 37, 0) = 37 gettimeofday({1299885101, 792289}, NULL) = 0 send(4, "Date: Fri, 11 Mar 2011 23:11:41 "..., 37, 0) = 37 send(4, "Content-type: text/plain\r\n", 26, 0) = 26 fstat64(5, {st_mode=S_IFREG|0755, st_size=2372, ...}) = 0 send(4, "Content-Length: 2372\r\n", 22, 0) = 22 send(4, "Last-Modified: Fri, 11 Mar 2011 "..., 46, 0) = 46 send(4, "\r\n", 2, 0) = 2 fstat64(5, {st_mode=S_IFREG|0755, st_size=2372, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x77343000 read(5, "# Copyright (c) 2011 The Chromiu"..., 16384) = 2372 read(5, "", 12288) = 0 send(4, "# Copyright (c) 2011 The Chromiu"..., 2372, 0) = 2372 read(5, "", 16384) = 0 close(5) = 0 munmap(0x77343000, 4096) = 0 close(4) = 0 select(4, [3], [], [], {0, 500000}) = 1 (in [3], left {0, 499991}) accept(3, {sa_family=AF_INET, sin_port=htons(42759), sin_addr=inet_addr("127.0.0.1")}, [16]) = 4 recv(4,

For convenience, this is the python http server code.

import SimpleHTTPServer import SocketServer

PORT = 8000

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler

httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT httpd.serve_forever()

Comment #1

Posted on Mar 14, 2011 by Grumpy Horse

(No comment was entered for this change.)

Comment #2

Posted on Mar 15, 2011 by Massive Giraffe

Problem found, but not easy to fix.

Chrome opens speculatively sockets to server it thinks it's about to connect to. These speculative opens are triggered by various situations. One of them is a DNS request. So, for instance, when requesting the URL "localhost:8000/xxx.html" two sockets are open, one to send the actual HTTP get, the other speculatively. This creates a deadlock, as apparently the browser will not complete sending the request until the speculative socket has successfully opened (this seems bogus but that's the way it is.)

The quick workaround is to have two server threads. This isn't a guarantee though, as there are cases in which more than one speculative socket may be opened.

Comment #3

Posted on Mar 15, 2011 by Massive Giraffe

(No comment was entered for this change.)

Comment #4

Posted on Mar 15, 2011 by Happy Bird

+jar

Comment #5

Posted on Mar 16, 2011 by Helpful Horse

I'm not sure if I'm understanding the comment above, but I think it is backwards.

The problem is that the server blocks, and won't serve additional content after getting a speculative request that is not IMMEDIATELY used. I think this is completely a server problem. The right answer is to fix the server (I think there is an outstanding bug to improve our current Python server to not be single threaded).

As an example: Suppose loading of resource A often leads to loading sub-resources B and C. For instance, B and C may be two consecutive script tags seen inside A. It may be that the browser always asks for those resources in the order A then B then C. The browser may, upon getting a request for resource A, preconnect to the server in anticipation of needing two connections. The bad news is that the server may accept both TCP/IP connection, responding to the SYN, as the OS does this deed (multithreaded!). The browser then tosses those connection into the open socket pool to use. The single-threaded server code may then latch onto one of these sockets (accept it), and begin to wait for the HTTP GET to come down the pike. The browser may instead try to push the HTTP GET down the other open socket... which will almost never be served (due to the python server blockage and trying to serve the other socket).

I assume that the server you use eventually times out (60 seconds???) and tosses the connection that never supplied the HTTP GET... and that proceeds to processes the real (pending) GET and things continue.

You can disable all of the pre-connecting (I think) by disabling DNS pre-resolution. This can be done by either a preference setting (via UI, recorded in prefs), or via a command line flag. Either of these should stop the pre-connects.

but... If you test on a slow network, the system will still (I think) create a second connection when the first one seems to be taking "too long" to connect. That too may instigate a second connection, that is not used, and leads to a deadlock. I don't think that flavor is hurting your test environment... but time will tell.

Comment #6

Posted on Mar 16, 2011 by Massive Ox

it's incredibly easy to mix multithreading into python's simple HTTP server:

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): def init(self, server_address, HandlerClass): HTTPServer.init(self, server_address, HandlerClass)

I made the web server in client/cros/httpd.py multithreaded in this way a few days ago. Or, one can use CherryPy or some other multithreaded python HTTP server. Why do we care about this?

Comment #7

Posted on Mar 16, 2011 by Happy Camel

(No comment was entered for this change.)

Comment #8

Posted on Mar 16, 2011 by Massive Ox

I don't even understand what the bug being reported here is, much less what I'm on the hook for fixing, or why I'm on the hook for fixing it.

WontFix'ing. If someone's writing code that's using a single-threaded python http server, then that person should fix his or her code.

Comment #9

Posted on Oct 5, 2012 by Happy Monkey

For reference, similar issue was previously worked around in chrome - crbug.com/57491

chrome test runners do now run with --disable-preconnect though, I guess to avoid these sorts of issues on the simplistic single-threaded test server

Comment #10

Posted on Oct 5, 2012 by Grumpy Wombat

I'm confused. crbug.com/57491 was marked as fixed 2 years ago. Do you think it could still be an issue?

Is --disable-preconnect a flag that I can pass to google-chrome? I don't see it mentioned in the help.

Comment #11

Posted on Mar 7, 2013 by Grumpy Hippo

(No comment was entered for this change.)

Comment #12

Posted on Mar 10, 2013 by Quick Rabbit

(No comment was entered for this change.)

Comment #13

Posted on Mar 14, 2013 by Happy Horse

Moved to: Issue chromium:195550

Status: Moved

Labels:
Type-Test Pri-2 Area-DesktopUI Sev-2 OS-Chrome M-12