diff options
author | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-07 19:46:24 +0000 |
---|---|---|
committer | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-07 19:46:24 +0000 |
commit | f8abf723d5fe8a85fade164dd030d4a13e0c7c29 (patch) | |
tree | 1d872fc493cee0c32daa7ea68fc02b8f348d42ac | |
parent | 8c728c0d72d060bb9d8ad74c066783215b36b087 (diff) | |
download | chromium_src-f8abf723d5fe8a85fade164dd030d4a13e0c7c29.zip chromium_src-f8abf723d5fe8a85fade164dd030d4a13e0c7c29.tar.gz chromium_src-f8abf723d5fe8a85fade164dd030d4a13e0c7c29.tar.bz2 |
Start at cleaning up BrowserMain().
BUG=none (or is there?)
TEST=trybots and tests
Review URL: http://codereview.chromium.org/2234006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51763 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/browser_main.cc | 544 | ||||
-rw-r--r-- | chrome/browser/browser_main.h | 92 | ||||
-rw-r--r-- | chrome/browser/browser_main_posix.cc | 228 | ||||
-rw-r--r-- | chrome/browser/browser_main_win.cc | 21 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 1 |
5 files changed, 525 insertions, 361 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 77caa2c..4ba9615 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -24,12 +24,11 @@ #include "base/string_util.h" #include "base/sys_string_conversions.h" #include "base/time.h" -#include "base/tracked_objects.h" #include "base/values.h" #include "build/build_config.h" +#include "chrome/browser/browser.h" #include "chrome/browser/browser_main_win.h" #include "chrome/browser/browser_init.h" -#include "chrome/browser/browser_list.h" #include "chrome/browser/browser_prefs.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_impl.h" @@ -81,15 +80,6 @@ #include "net/socket/client_socket_pool_base.h" #include "net/spdy/spdy_session_pool.h" -#if defined(OS_POSIX) -// TODO(port): get rid of this include. It's used just to provide declarations -// and stub definitions for classes we encouter during the porting effort. -#include <errno.h> -#include <signal.h> -#include <sys/resource.h> -#include "base/eintr_wrapper.h" -#endif - #if defined(USE_LINUX_BREAKPAD) #include "base/linux_util.h" #include "chrome/app/breakpad_linux.h" @@ -133,7 +123,6 @@ #include "chrome/installer/util/version.h" #include "net/base/net_util.h" #include "net/base/sdch_manager.h" -#include "net/base/winsock_init.h" #include "net/socket/ssl_client_socket_nss_factory.h" #include "printing/printed_document.h" #include "sandbox/src/sandbox.h" @@ -164,6 +153,178 @@ #include "chrome/browser/views/browser_dialogs.h" #endif +// BrowserMainParts ------------------------------------------------------------ + +BrowserMainParts::BrowserMainParts(const MainFunctionParams& parameters) + : parameters_(parameters), + parsed_command_line_(parameters.command_line_) { +} + +// BrowserMainParts: EarlyInitialization() and related ------------------------- + +void BrowserMainParts::EarlyInitialization() { + PreEarlyInitialization(); + + ConnectionFieldTrial(); + SocketTimeoutFieldTrial(); + SpdyFieldTrial(); + InitializeSSL(); // TODO(viettrungluu): move to platform-specific method(s) + + PostEarlyInitialization(); +} + +// This is an A/B test for the maximum number of persistent connections per +// host. Currently Chrome, Firefox, and IE8 have this value set at 6. Safari +// uses 4, and Fasterfox (a plugin for Firefox that supposedly configures it to +// run faster) uses 8. We would like to see how much of an effect this value has +// on browsing. Too large a value might cause us to run into SYN flood detection +// mechanisms. +void BrowserMainParts::ConnectionFieldTrial() { + const FieldTrial::Probability kConnDivisor = 100; + const FieldTrial::Probability kConn16 = 10; // 10% probability + const FieldTrial::Probability kRemainingConn = 30; // 30% probability + + scoped_refptr<FieldTrial> conn_trial = + new FieldTrial("ConnCountImpact", kConnDivisor); + + const int conn_16 = conn_trial->AppendGroup("_conn_count_16", kConn16); + const int conn_4 = conn_trial->AppendGroup("_conn_count_4", kRemainingConn); + const int conn_8 = conn_trial->AppendGroup("_conn_count_8", kRemainingConn); + const int conn_6 = conn_trial->AppendGroup("_conn_count_6", + FieldTrial::kAllRemainingProbability); + + const int conn_trial_grp = conn_trial->group(); + + if (conn_trial_grp == conn_4) { + net::HttpNetworkSession::set_max_sockets_per_group(4); + } else if (conn_trial_grp == conn_6) { + // This (6) is the current default value. + net::HttpNetworkSession::set_max_sockets_per_group(6); + } else if (conn_trial_grp == conn_8) { + net::HttpNetworkSession::set_max_sockets_per_group(8); + } else if (conn_trial_grp == conn_16) { + net::HttpNetworkSession::set_max_sockets_per_group(16); + } else { + NOTREACHED(); + } +} + +// A/B test for determining a value for unused socket timeout. Currently the +// timeout defaults to 10 seconds. Having this value set too low won't allow us +// to take advantage of idle sockets. Setting it to too high could possibly +// result in more ERR_CONNECT_RESETs, requiring one RTT to receive the RST +// packet and possibly another RTT to re-establish the connection. +void BrowserMainParts::SocketTimeoutFieldTrial() { + const FieldTrial::Probability kIdleSktToDivisor = 100; // Idle socket timeout + const FieldTrial::Probability kSktToProb = 25; // 25% probability + + scoped_refptr<FieldTrial> socket_timeout_trial = + new FieldTrial("IdleSktToImpact", kIdleSktToDivisor); + + const int socket_timeout_5 = + socket_timeout_trial->AppendGroup("_idle_timeout_5", kSktToProb); + const int socket_timeout_10 = + socket_timeout_trial->AppendGroup("_idle_timeout_10", kSktToProb); + const int socket_timeout_20 = + socket_timeout_trial->AppendGroup("_idle_timeout_20", kSktToProb); + const int socket_timeout_60 = + socket_timeout_trial->AppendGroup("_idle_timeout_60", + FieldTrial::kAllRemainingProbability); + + const int idle_to_trial_grp = socket_timeout_trial->group(); + + if (idle_to_trial_grp == socket_timeout_5) { + net::ClientSocketPool::set_unused_idle_socket_timeout(5); + } else if (idle_to_trial_grp == socket_timeout_10) { + // This (10 seconds) is the current default value. + net::ClientSocketPool::set_unused_idle_socket_timeout(10); + } else if (idle_to_trial_grp == socket_timeout_20) { + net::ClientSocketPool::set_unused_idle_socket_timeout(20); + } else if (idle_to_trial_grp == socket_timeout_60) { + net::ClientSocketPool::set_unused_idle_socket_timeout(60); + } else { + NOTREACHED(); + } +} + +// When --use-spdy not set, users will be in A/B test for spdy. +// group A (_npn_with_spdy): this means npn and spdy are enabled. In case server +// supports spdy, browser will use spdy. +// group B (_npn_with_http): this means npn is enabled but spdy won't be used. +// Http is still used for all requests. +// default group: no npn or spdy is involved. The "old" non-spdy +// chrome behavior. +void BrowserMainParts::SpdyFieldTrial() { + bool is_spdy_trial = false; + if (parsed_command_line().HasSwitch(switches::kUseSpdy)) { + std::string spdy_mode = + parsed_command_line().GetSwitchValueASCII(switches::kUseSpdy); + net::HttpNetworkLayer::EnableSpdy(spdy_mode); + } else { + const FieldTrial::Probability kSpdyDivisor = 1000; + // TODO(lzheng): Increase these values to enable more spdy tests. + // To enable 100% npn_with_spdy, set npnhttp_probability = 0 and set + // npnspdy_probability = FieldTrial::kAllRemainingProbability. + FieldTrial::Probability npnhttp_probability = 250; // 25% + FieldTrial::Probability npnspdy_probability = 250; // 25% +#if defined(OS_WIN) + // Enable the A/B test for SxS. SxS is only available on windows + std::wstring channel; + if (BrowserDistribution::GetDistribution()->GetChromeChannel(&channel) && + channel == GoogleChromeSxSDistribution::ChannelName()) { + npnhttp_probability = 500; + npnspdy_probability = 500; + } +#endif + scoped_refptr<FieldTrial> trial = + new FieldTrial("SpdyImpact", kSpdyDivisor); + // npn with only http support, no spdy. + int npn_http_grp = + trial->AppendGroup("_npn_with_http", npnhttp_probability); + // npn with spdy support. + int npn_spdy_grp = + trial->AppendGroup("_npn_with_spdy", npnspdy_probability); + int trial_grp = trial->group(); + if (trial_grp == npn_http_grp) { + is_spdy_trial = true; + net::HttpNetworkLayer::EnableSpdy("npn-http"); + } else if (trial_grp == npn_spdy_grp) { + is_spdy_trial = true; + net::HttpNetworkLayer::EnableSpdy("npn"); + } else { + CHECK(!is_spdy_trial); + } + } +} + +// TODO(viettrungluu): move to platform-specific methods +void BrowserMainParts::InitializeSSL() { + // Use NSS for SSL by default. +#if defined(OS_MACOSX) + // The default client socket factory uses NSS for SSL by default on Mac. + if (parsed_command_line().HasSwitch(switches::kUseSystemSSL)) { + net::ClientSocketFactory::SetSSLClientSocketFactory( + net::SSLClientSocketMacFactory); + } else { + // We want to be sure to init NSPR on the main thread. + base::EnsureNSPRInit(); + } +#elif defined(OS_WIN) + // Because of a build system issue (http://crbug.com/43461), the default + // client socket factory uses SChannel (the system SSL library) for SSL by + // default on Windows. + if (!parsed_command_line().HasSwitch(switches::kUseSystemSSL)) { + net::ClientSocketFactory::SetSSLClientSocketFactory( + net::SSLClientSocketNSSFactory); + // We want to be sure to init NSPR on the main thread. + base::EnsureNSPRInit(); + } +#endif +} + +// ----------------------------------------------------------------------------- +// TODO(viettrungluu): move more/rest of BrowserMain() into above structure + namespace { // This function provides some ways to test crash and assertion handling @@ -200,140 +361,6 @@ void RunUIMessageLoop(BrowserProcess* browser_process) { #endif } -#if defined(OS_POSIX) -// See comment in BrowserMain, where sigaction is called. -void SIGCHLDHandler(int signal) { -} - -int g_shutdown_pipe_write_fd = -1; -int g_shutdown_pipe_read_fd = -1; - -// Common code between SIG{HUP, INT, TERM}Handler. -void GracefulShutdownHandler(int signal) { - // Reinstall the default handler. We had one shot at graceful shutdown. - struct sigaction action; - memset(&action, 0, sizeof(action)); - action.sa_handler = SIG_DFL; - RAW_CHECK(sigaction(signal, &action, NULL) == 0); - - RAW_CHECK(g_shutdown_pipe_write_fd != -1); - RAW_CHECK(g_shutdown_pipe_read_fd != -1); - size_t bytes_written = 0; - do { - int rv = HANDLE_EINTR( - write(g_shutdown_pipe_write_fd, - reinterpret_cast<const char*>(&signal) + bytes_written, - sizeof(signal) - bytes_written)); - RAW_CHECK(rv >= 0); - bytes_written += rv; - } while (bytes_written < sizeof(signal)); - - RAW_LOG(INFO, - "Successfully wrote to shutdown pipe, resetting signal handler."); -} - -// See comment in BrowserMain, where sigaction is called. -void SIGHUPHandler(int signal) { - RAW_CHECK(signal == SIGHUP); - RAW_LOG(INFO, "Handling SIGHUP."); - GracefulShutdownHandler(signal); -} - -// See comment in BrowserMain, where sigaction is called. -void SIGINTHandler(int signal) { - RAW_CHECK(signal == SIGINT); - RAW_LOG(INFO, "Handling SIGINT."); - GracefulShutdownHandler(signal); -} - -// See comment in BrowserMain, where sigaction is called. -void SIGTERMHandler(int signal) { - RAW_CHECK(signal == SIGTERM); - RAW_LOG(INFO, "Handling SIGTERM."); - GracefulShutdownHandler(signal); -} - -class ShutdownDetector : public PlatformThread::Delegate { - public: - explicit ShutdownDetector(int shutdown_fd); - - virtual void ThreadMain(); - - private: - const int shutdown_fd_; - - DISALLOW_COPY_AND_ASSIGN(ShutdownDetector); -}; - -ShutdownDetector::ShutdownDetector(int shutdown_fd) - : shutdown_fd_(shutdown_fd) { - CHECK_NE(shutdown_fd_, -1); -} - -void ShutdownDetector::ThreadMain() { - int signal; - size_t bytes_read = 0; - ssize_t ret; - do { - ret = HANDLE_EINTR( - read(shutdown_fd_, - reinterpret_cast<char*>(&signal) + bytes_read, - sizeof(signal) - bytes_read)); - if (ret < 0) { - NOTREACHED() << "Unexpected error: " << strerror(errno); - break; - } else if (ret == 0) { - NOTREACHED() << "Unexpected closure of shutdown pipe."; - break; - } - bytes_read += ret; - } while (bytes_read < sizeof(signal)); - - LOG(INFO) << "Handling shutdown for signal " << signal << "."; - - if (!ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableFunction(BrowserList::CloseAllBrowsersAndExit))) { - // Without a UI thread to post the exit task to, there aren't many - // options. Raise the signal again. The default handler will pick it up - // and cause an ungraceful exit. - RAW_LOG(WARNING, "No UI thread, exiting ungracefully."); - kill(getpid(), signal); - - // The signal may be handled on another thread. Give that a chance to - // happen. - sleep(3); - - // We really should be dead by now. For whatever reason, we're not. Exit - // immediately, with the exit status set to the signal number with bit 8 - // set. On the systems that we care about, this exit status is what is - // normally used to indicate an exit by this signal's default handler. - // This mechanism isn't a de jure standard, but even in the worst case, it - // should at least result in an immediate exit. - RAW_LOG(WARNING, "Still here, exiting really ungracefully."); - _exit(signal | (1 << 7)); - } -} - -// Sets the file descriptor soft limit to |max_descriptors| or the OS hard -// limit, whichever is lower. -void SetFileDescriptorLimit(unsigned int max_descriptors) { - struct rlimit limits; - if (getrlimit(RLIMIT_NOFILE, &limits) == 0) { - unsigned int new_limit = max_descriptors; - if (limits.rlim_max > 0 && limits.rlim_max < max_descriptors) { - new_limit = limits.rlim_max; - } - limits.rlim_cur = new_limit; - if (setrlimit(RLIMIT_NOFILE, &limits) != 0) { - PLOG(INFO) << "Failed to set file descriptor limit"; - } - } else { - PLOG(INFO) << "Failed to get file descriptor limit"; - } -} -#endif - void AddFirstRunNewTabs(BrowserInit* browser_init, const std::vector<GURL>& new_tabs) { for (std::vector<GURL>::const_iterator it = new_tabs.begin(); @@ -684,6 +711,12 @@ DLLEXPORT void __cdecl RelaunchChromeBrowserWithNewCommandLineIfNeeded() { // Main routine for running as the Browser process. int BrowserMain(const MainFunctionParams& parameters) { + scoped_ptr<BrowserMainParts> + parts(BrowserMainParts::CreateBrowserMainParts(parameters)); + + parts->EarlyInitialization(); + + // TODO(viettrungluu): put the remainder into BrowserMainParts const CommandLine& parsed_command_line = parameters.command_line_; base::ScopedNSAutoreleasePool* pool = parameters.autorelease_pool_; @@ -694,205 +727,6 @@ int BrowserMain(const MainFunctionParams& parameters) { // TODO(beng, brettw): someday, break this out into sub functions with well // defined roles (e.g. pre/post-profile startup, etc). -#ifdef TRACK_ALL_TASK_OBJECTS - // Start tracking the creation and deletion of Task instance. - // This construction MUST be done before main_message_loop, so that it is - // destroyed after the main_message_loop. - tracked_objects::AutoTracking tracking_objects; -#endif - -#if defined(OS_POSIX) - // We need to accept SIGCHLD, even though our handler is a no-op because - // otherwise we cannot wait on children. (According to POSIX 2001.) - struct sigaction action; - memset(&action, 0, sizeof(action)); - action.sa_handler = SIGCHLDHandler; - CHECK(sigaction(SIGCHLD, &action, NULL) == 0); - - // If adding to this list of signal handlers, note the new signal probably - // needs to be reset in child processes. See - // base/process_util_posix.cc:LaunchApp - - // We need to handle SIGTERM, because that is how many POSIX-based distros ask - // processes to quit gracefully at shutdown time. - memset(&action, 0, sizeof(action)); - action.sa_handler = SIGTERMHandler; - CHECK(sigaction(SIGTERM, &action, NULL) == 0); - // Also handle SIGINT - when the user terminates the browser via Ctrl+C. - // If the browser process is being debugged, GDB will catch the SIGINT first. - action.sa_handler = SIGINTHandler; - CHECK(sigaction(SIGINT, &action, NULL) == 0); - // And SIGHUP, for when the terminal disappears. On shutdown, many Linux - // distros send SIGHUP, SIGTERM, and then SIGKILL. - action.sa_handler = SIGHUPHandler; - CHECK(sigaction(SIGHUP, &action, NULL) == 0); - - const std::string fd_limit_string = - parsed_command_line.GetSwitchValueASCII(switches::kFileDescriptorLimit); - int fd_limit = 0; - if (!fd_limit_string.empty()) { - StringToInt(fd_limit_string, &fd_limit); - } -#if defined(OS_MACOSX) - // We use quite a few file descriptors for our IPC, and the default limit on - // the Mac is low (256), so bump it up if there is no explicit override. - if (fd_limit == 0) { - fd_limit = 1024; - } -#endif // OS_MACOSX - if (fd_limit > 0) { - SetFileDescriptorLimit(fd_limit); - } -#endif // OS_POSIX - -#if defined(OS_WIN) - // Initialize Winsock. - net::EnsureWinsockInit(); -#endif // defined(OS_WIN) - - // Initialize statistical testing infrastructure for entire browser. - FieldTrialList field_trial; - - // This is an A/B test for the maximum number of persistent connections per - // host. Currently Chrome, Firefox, and IE8 have this value set at 6. - // Safari uses 4, and Fasterfox (a plugin for Firefox that supposedly - // configures it to run faster) uses 8. We would like to see how much of an - // effect this value has on browsing. Too large a value might cause us to - // run into SYN flood detection mechanisms. - const FieldTrial::Probability kConnDivisor = 100; - const FieldTrial::Probability kConn16 = 10; // 10% probability - const FieldTrial::Probability kRemainingConn = 30; // 30% probability - - scoped_refptr<FieldTrial> conn_trial = - new FieldTrial("ConnCountImpact", kConnDivisor); - - const int conn_16 = conn_trial->AppendGroup("_conn_count_16", kConn16); - const int conn_4 = conn_trial->AppendGroup("_conn_count_4", kRemainingConn); - const int conn_8 = conn_trial->AppendGroup("_conn_count_8", kRemainingConn); - const int conn_6 = conn_trial->AppendGroup("_conn_count_6", - FieldTrial::kAllRemainingProbability); - - const int conn_trial_grp = conn_trial->group(); - - if (conn_trial_grp == conn_4) { - net::HttpNetworkSession::set_max_sockets_per_group(4); - } else if (conn_trial_grp == conn_6) { - // This (6) is the current default value. - net::HttpNetworkSession::set_max_sockets_per_group(6); - } else if (conn_trial_grp == conn_8) { - net::HttpNetworkSession::set_max_sockets_per_group(8); - } else if (conn_trial_grp == conn_16) { - net::HttpNetworkSession::set_max_sockets_per_group(16); - } else { - NOTREACHED(); - } - - // A/B test for determining a value for unused socket timeout. Currently the - // timeout defaults to 10 seconds. Having this value set too low won't allow - // us to take advantage of idle sockets. Setting it to too high could - // possibly result in more ERR_CONNECT_RESETs, requiring one RTT to receive - // the RST packet and possibly another RTT to re-establish the connection. - const FieldTrial::Probability kIdleSktToDivisor = 100; // Idle socket timeout - const FieldTrial::Probability kSktToProb = 25; // 25% probability - - scoped_refptr<FieldTrial> socket_timeout_trial = - new FieldTrial("IdleSktToImpact", kIdleSktToDivisor); - - const int socket_timeout_5 = - socket_timeout_trial->AppendGroup("_idle_timeout_5", kSktToProb); - const int socket_timeout_10 = - socket_timeout_trial->AppendGroup("_idle_timeout_10", kSktToProb); - const int socket_timeout_20 = - socket_timeout_trial->AppendGroup("_idle_timeout_20", kSktToProb); - const int socket_timeout_60 = - socket_timeout_trial->AppendGroup("_idle_timeout_60", - FieldTrial::kAllRemainingProbability); - - const int idle_to_trial_grp = socket_timeout_trial->group(); - - if (idle_to_trial_grp == socket_timeout_5) { - net::ClientSocketPool::set_unused_idle_socket_timeout(5); - } else if (idle_to_trial_grp == socket_timeout_10) { - // This (10 seconds) is the current default value. - net::ClientSocketPool::set_unused_idle_socket_timeout(10); - } else if (idle_to_trial_grp == socket_timeout_20) { - net::ClientSocketPool::set_unused_idle_socket_timeout(20); - } else if (idle_to_trial_grp == socket_timeout_60) { - net::ClientSocketPool::set_unused_idle_socket_timeout(60); - } else { - NOTREACHED(); - } - - // When --use-spdy not set, users will be in A/B test for spdy. - // group A (_npn_with_spdy): this means npn and spdy are enabled. In - // case server supports spdy, browser will use spdy. - // group B (_npn_with_http): this means npn is enabled but spdy - // won't be used. Http is still used for all requests. - // default group: no npn or spdy is involved. The "old" non-spdy chrome - // behavior. - bool is_spdy_trial = false; - if (parsed_command_line.HasSwitch(switches::kUseSpdy)) { - std::string spdy_mode = - parsed_command_line.GetSwitchValueASCII(switches::kUseSpdy); - net::HttpNetworkLayer::EnableSpdy(spdy_mode); - } else { - const FieldTrial::Probability kSpdyDivisor = 1000; - // TODO(lzheng): Increase these values to enable more spdy tests. - // To enable 100% npn_with_spdy, set npnhttp_probability = 0 and set - // npnspdy_probability = FieldTrial::kAllRemainingProbability. - FieldTrial::Probability npnhttp_probability = 250; // 25% - FieldTrial::Probability npnspdy_probability = 250; // 25% -#if defined(OS_WIN) - // Enable the A/B test for SxS. SxS is only available on windows - std::wstring channel; - if (BrowserDistribution::GetDistribution()->GetChromeChannel(&channel) && - channel == GoogleChromeSxSDistribution::ChannelName()) { - npnhttp_probability = 500; - npnspdy_probability = 500; - } -#endif - scoped_refptr<FieldTrial> trial = - new FieldTrial("SpdyImpact", kSpdyDivisor); - // npn with only http support, no spdy. - int npn_http_grp = - trial->AppendGroup("_npn_with_http", npnhttp_probability); - // npn with spdy support. - int npn_spdy_grp = - trial->AppendGroup("_npn_with_spdy", npnspdy_probability); - int trial_grp = trial->group(); - if (trial_grp == npn_http_grp) { - is_spdy_trial = true; - net::HttpNetworkLayer::EnableSpdy("npn-http"); - } else if (trial_grp == npn_spdy_grp) { - is_spdy_trial = true; - net::HttpNetworkLayer::EnableSpdy("npn"); - } else { - CHECK(!is_spdy_trial); - } - } - - // Use NSS for SSL by default. -#if defined(OS_MACOSX) - // The default client socket factory uses NSS for SSL by default on Mac. - if (parsed_command_line.HasSwitch(switches::kUseSystemSSL)) { - net::ClientSocketFactory::SetSSLClientSocketFactory( - net::SSLClientSocketMacFactory); - } else { - // We want to be sure to init NSPR on the main thread. - base::EnsureNSPRInit(); - } -#elif defined(OS_WIN) - // Because of a build system issue (http://crbug.com/43461), the default - // client socket factory uses SChannel (the system SSL library) for SSL by - // default on Windows. - if (!parsed_command_line.HasSwitch(switches::kUseSystemSSL)) { - net::ClientSocketFactory::SetSSLClientSocketFactory( - net::SSLClientSocketNSSFactory); - // We want to be sure to init NSPR on the main thread. - base::EnsureNSPRInit(); - } -#endif - // Do platform-specific things (such as finishing initializing Cocoa) // prior to instantiating the message loop. This could be turned into a // broadcast notification. @@ -911,22 +745,9 @@ int BrowserMain(const MainFunctionParams& parameters) { // Register the main thread by instantiating it, but don't call any methods. ChromeThread main_thread(ChromeThread::UI, MessageLoop::current()); -#if defined(OS_POSIX) - int pipefd[2]; - int ret = pipe(pipefd); - if (ret < 0) { - PLOG(DFATAL) << "Failed to create pipe"; - } else { - g_shutdown_pipe_read_fd = pipefd[0]; - g_shutdown_pipe_write_fd = pipefd[1]; - const size_t kShutdownDetectorThreadStackSize = 4096; - if (!PlatformThread::CreateNonJoinable( - kShutdownDetectorThreadStackSize, - new ShutdownDetector(g_shutdown_pipe_read_fd))) { - LOG(DFATAL) << "Failed to create shutdown detector task."; - } - } -#endif // defined(OS_POSIX) + + // TODO(viettrungluu): temporary while I refactor BrowserMain() + parts->TemporaryPosix_1(); FilePath user_data_dir; #if defined(OS_WIN) @@ -1041,6 +862,7 @@ int BrowserMain(const MainFunctionParams& parameters) { first_run_ui_bypass = true; } + // TODO(viettrungluu): why don't we run this earlier? if (!parsed_command_line.HasSwitch(switches::kNoErrorDialogs)) WarnAboutMinimumSystemRequirements(); diff --git a/chrome/browser/browser_main.h b/chrome/browser/browser_main.h index d11d4d9..d23a7e5 100644 --- a/chrome/browser/browser_main.h +++ b/chrome/browser/browser_main.h @@ -5,9 +5,101 @@ #ifndef CHROME_BROWSER_BROWSER_MAIN_H_ #define CHROME_BROWSER_BROWSER_MAIN_H_ +#include "base/basictypes.h" +#include "base/field_trial.h" +#include "base/tracked_objects.h" + +class CommandLine; struct MainFunctionParams; class MetricsService; +// BrowserMainParts: +// This class contains different "stages" to be executed in |BrowserMain()|, +// mostly initialization. This is made into a class rather than just functions +// so each stage can create and maintain state. Each part is represented by a +// single method (e.g., "EarlyInitialization()"), which does the following: +// - calls a method (e.g., "PreEarlyInitialization()") which individual +// platforms can override to provide platform-specific code which is to be +// executed before the common code; +// - calls various methods for things common to all platforms (for that given +// stage); and +// - calls a method (e.g., "PostEarlyInitialization()") for platform-specific +// code to be called after the common code. +// As indicated above, platforms should override the default "Pre...()" and +// "Post...()" methods when necessary; they need not call the superclass's +// implementation (which is empty). +// +// Parts: +// - EarlyInitialization: things which should be done as soon as possible on +// program start (such as setting up signal handlers) and things to be done +// at some generic time before the start of the main message loop. +// - (more to come) +class BrowserMainParts { + public: + // This static method is to be implemented by each platform and should + // instantiate the appropriate subclass. + static BrowserMainParts* CreateBrowserMainParts( + const MainFunctionParams& parameters); + + // Parts to be called by |BrowserMain()|. + void EarlyInitialization(); + + // TODO(viettrungluu): This currently contains (POSIX) initialization done + // later than "EarlyInitialization()" but dependent on it. Once the + // refactoring includes that later stage, this should be put in some more + // generic platform-dependent method. + void TemporaryPosix_1() {} + + protected: + explicit BrowserMainParts(const MainFunctionParams& parameters); + + // Methods to be overridden to provide platform-specific code; these + // correspond to the "parts" above. + virtual void PreEarlyInitialization() {} + virtual void PostEarlyInitialization() {} + + // Accessors for data members (below) ---------------------------------------- + const MainFunctionParams& parameters() const { + return parameters_; + } + const CommandLine& parsed_command_line() const { + return parsed_command_line_; + } + + private: + // Methods for |EarlyInitialization()| --------------------------------------- + + // A/B test for the maximum number of persistent connections per host. + void ConnectionFieldTrial(); + + // A/B test for determining a value for unused socket timeout. + void SocketTimeoutFieldTrial(); + + // A/B test for spdy when --use-spdy not set. + void SpdyFieldTrial(); + + // Used to initialize NSPR where appropriate. + void InitializeSSL(); + + // Members initialized on construction --------------------------------------- + + const MainFunctionParams& parameters_; + const CommandLine& parsed_command_line_; + +#if defined(TRACK_ALL_TASK_OBJECTS) + // Creating this object starts tracking the creation and deletion of Task + // instance. This MUST be done before main_message_loop, so that it is + // destroyed after the main_message_loop. + tracked_objects::AutoTracking tracking_objects_; +#endif + + // Statistical testing infrastructure for the entire browser. + FieldTrialList field_trial_; + + DISALLOW_COPY_AND_ASSIGN(BrowserMainParts); +}; + + // Perform platform-specific work that needs to be done before the main // message loop is created, initialized, and entered. void WillInitializeMainMessageLoop(const MainFunctionParams& parameters); diff --git a/chrome/browser/browser_main_posix.cc b/chrome/browser/browser_main_posix.cc new file mode 100644 index 0000000..f9a5df2 --- /dev/null +++ b/chrome/browser/browser_main_posix.cc @@ -0,0 +1,228 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <errno.h> +#include <signal.h> +#include <sys/resource.h> + +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_main.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/common/chrome_switches.h" + +namespace { + +// See comment in |PreEarlyInitialization()|, where sigaction is called. +void SIGCHLDHandler(int signal) { +} + +int g_shutdown_pipe_write_fd = -1; +int g_shutdown_pipe_read_fd = -1; + +// Common code between SIG{HUP, INT, TERM}Handler. +void GracefulShutdownHandler(int signal) { + // Reinstall the default handler. We had one shot at graceful shutdown. + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_DFL; + RAW_CHECK(sigaction(signal, &action, NULL) == 0); + + RAW_CHECK(g_shutdown_pipe_write_fd != -1); + RAW_CHECK(g_shutdown_pipe_read_fd != -1); + size_t bytes_written = 0; + do { + int rv = HANDLE_EINTR( + write(g_shutdown_pipe_write_fd, + reinterpret_cast<const char*>(&signal) + bytes_written, + sizeof(signal) - bytes_written)); + RAW_CHECK(rv >= 0); + bytes_written += rv; + } while (bytes_written < sizeof(signal)); + + RAW_LOG(INFO, + "Successfully wrote to shutdown pipe, resetting signal handler."); +} + +// See comment in |PreEarlyInitialization()|, where sigaction is called. +void SIGHUPHandler(int signal) { + RAW_CHECK(signal == SIGHUP); + RAW_LOG(INFO, "Handling SIGHUP."); + GracefulShutdownHandler(signal); +} + +// See comment in |PreEarlyInitialization()|, where sigaction is called. +void SIGINTHandler(int signal) { + RAW_CHECK(signal == SIGINT); + RAW_LOG(INFO, "Handling SIGINT."); + GracefulShutdownHandler(signal); +} + +// See comment in |PreEarlyInitialization()|, where sigaction is called. +void SIGTERMHandler(int signal) { + RAW_CHECK(signal == SIGTERM); + RAW_LOG(INFO, "Handling SIGTERM."); + GracefulShutdownHandler(signal); +} + +class ShutdownDetector : public PlatformThread::Delegate { + public: + explicit ShutdownDetector(int shutdown_fd); + + virtual void ThreadMain(); + + private: + const int shutdown_fd_; + + DISALLOW_COPY_AND_ASSIGN(ShutdownDetector); +}; + +ShutdownDetector::ShutdownDetector(int shutdown_fd) + : shutdown_fd_(shutdown_fd) { + CHECK_NE(shutdown_fd_, -1); +} + +void ShutdownDetector::ThreadMain() { + int signal; + size_t bytes_read = 0; + ssize_t ret; + do { + ret = HANDLE_EINTR( + read(shutdown_fd_, + reinterpret_cast<char*>(&signal) + bytes_read, + sizeof(signal) - bytes_read)); + if (ret < 0) { + NOTREACHED() << "Unexpected error: " << strerror(errno); + break; + } else if (ret == 0) { + NOTREACHED() << "Unexpected closure of shutdown pipe."; + break; + } + bytes_read += ret; + } while (bytes_read < sizeof(signal)); + + LOG(INFO) << "Handling shutdown for signal " << signal << "."; + + if (!ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(BrowserList::CloseAllBrowsersAndExit))) { + // Without a UI thread to post the exit task to, there aren't many + // options. Raise the signal again. The default handler will pick it up + // and cause an ungraceful exit. + RAW_LOG(WARNING, "No UI thread, exiting ungracefully."); + kill(getpid(), signal); + + // The signal may be handled on another thread. Give that a chance to + // happen. + sleep(3); + + // We really should be dead by now. For whatever reason, we're not. Exit + // immediately, with the exit status set to the signal number with bit 8 + // set. On the systems that we care about, this exit status is what is + // normally used to indicate an exit by this signal's default handler. + // This mechanism isn't a de jure standard, but even in the worst case, it + // should at least result in an immediate exit. + RAW_LOG(WARNING, "Still here, exiting really ungracefully."); + _exit(signal | (1 << 7)); + } +} + +// Sets the file descriptor soft limit to |max_descriptors| or the OS hard +// limit, whichever is lower. +void SetFileDescriptorLimit(unsigned int max_descriptors) { + struct rlimit limits; + if (getrlimit(RLIMIT_NOFILE, &limits) == 0) { + unsigned int new_limit = max_descriptors; + if (limits.rlim_max > 0 && limits.rlim_max < max_descriptors) { + new_limit = limits.rlim_max; + } + limits.rlim_cur = new_limit; + if (setrlimit(RLIMIT_NOFILE, &limits) != 0) { + PLOG(INFO) << "Failed to set file descriptor limit"; + } + } else { + PLOG(INFO) << "Failed to get file descriptor limit"; + } +} + +} // namespace + +// BrowserMainPartsPosix ------------------------------------------------------- + +class BrowserMainPartsPosix : public BrowserMainParts { + public: + explicit BrowserMainPartsPosix(const MainFunctionParams& parameters) + : BrowserMainParts(parameters) {} + + protected: + virtual void PreEarlyInitialization() { + // We need to accept SIGCHLD, even though our handler is a no-op because + // otherwise we cannot wait on children. (According to POSIX 2001.) + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = SIGCHLDHandler; + CHECK(sigaction(SIGCHLD, &action, NULL) == 0); + + // If adding to this list of signal handlers, note the new signal probably + // needs to be reset in child processes. See + // base/process_util_posix.cc:LaunchApp + + // We need to handle SIGTERM, because that is how many POSIX-based distros + // ask processes to quit gracefully at shutdown time. + memset(&action, 0, sizeof(action)); + action.sa_handler = SIGTERMHandler; + CHECK(sigaction(SIGTERM, &action, NULL) == 0); + // Also handle SIGINT - when the user terminates the browser via Ctrl+C. If + // the browser process is being debugged, GDB will catch the SIGINT first. + action.sa_handler = SIGINTHandler; + CHECK(sigaction(SIGINT, &action, NULL) == 0); + // And SIGHUP, for when the terminal disappears. On shutdown, many Linux + // distros send SIGHUP, SIGTERM, and then SIGKILL. + action.sa_handler = SIGHUPHandler; + CHECK(sigaction(SIGHUP, &action, NULL) == 0); + + const std::string fd_limit_string = + parsed_command_line().GetSwitchValueASCII( + switches::kFileDescriptorLimit); + int fd_limit = 0; + if (!fd_limit_string.empty()) { + StringToInt(fd_limit_string, &fd_limit); + } +#if defined(OS_MACOSX) + // We use quite a few file descriptors for our IPC, and the default limit on + // the Mac is low (256), so bump it up if there is no explicit override. + if (fd_limit == 0) { + fd_limit = 1024; + } +#endif // OS_MACOSX + if (fd_limit > 0) + SetFileDescriptorLimit(fd_limit); + } + + void TemporaryPosix_1() { + int pipefd[2]; + int ret = pipe(pipefd); + if (ret < 0) { + PLOG(DFATAL) << "Failed to create pipe"; + } else { + g_shutdown_pipe_read_fd = pipefd[0]; + g_shutdown_pipe_write_fd = pipefd[1]; + const size_t kShutdownDetectorThreadStackSize = 4096; + if (!PlatformThread::CreateNonJoinable( + kShutdownDetectorThreadStackSize, + new ShutdownDetector(g_shutdown_pipe_read_fd))) { + LOG(DFATAL) << "Failed to create shutdown detector task."; + } + } + } +}; + +// static +BrowserMainParts* BrowserMainParts::CreateBrowserMainParts( + const MainFunctionParams& parameters) { + return new BrowserMainPartsPosix(parameters); +} diff --git a/chrome/browser/browser_main_win.cc b/chrome/browser/browser_main_win.cc index 0f4fc35..fdfb413 100644 --- a/chrome/browser/browser_main_win.cc +++ b/chrome/browser/browser_main_win.cc @@ -29,6 +29,7 @@ #include "chrome/installer/util/shell_util.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" +#include "net/base/winsock_init.h" #include "views/focus/accelerator_handler.h" #include "views/window/window.h" @@ -202,3 +203,23 @@ bool CheckMachineLevelInstall() { } return false; } + +// BrowserMainPartsWin --------------------------------------------------------- + +class BrowserMainPartsWin : public BrowserMainParts { + public: + explicit BrowserMainPartsWin(const MainFunctionParams& parameters) + : BrowserMainParts(parameters) {} + + protected: + virtual void PreEarlyInitialization() { + // Initialize Winsock. + net::EnsureWinsockInit(); + } +}; + +// static +BrowserMainParts* BrowserMainParts::CreateBrowserMainParts( + const MainFunctionParams& parameters) { + return new BrowserMainPartsWin(parameters); +} diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 801b533..764ffc3 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -269,6 +269,7 @@ 'browser/browser_main.cc', 'browser/browser_main_gtk.cc', 'browser/browser_main_mac.mm', + 'browser/browser_main_posix.cc', 'browser/browser_main_win.cc', 'browser/browser_main_win.h', 'browser/browser_prefs.cc', |