diff --git a/changes/bufferevent-support b/changes/bufferevent-support index 22ee50ebff..e39c36f13b 100644 --- a/changes/bufferevent-support +++ b/changes/bufferevent-support @@ -6,4 +6,7 @@ flag. Using this feature will make our networking code more flexible, lets us stack layers on each other, and let us use more efficient zero-copy transports where available. + - As an experimental feature, when using the "bufferevents" buffered + IO backend, Tor can try to use Windows's IOCP networking API. This + is off by default. To turn it on, add "DisableIOCP 0" to your torrc. diff --git a/changes/cpudetect b/changes/cpudetect new file mode 100644 index 0000000000..eeaa492f58 --- /dev/null +++ b/changes/cpudetect @@ -0,0 +1,3 @@ + o Minor features + - If you set the NumCPUs option to 0, Tor will try to detect how many + CPUs you have. This is the new default behavior. diff --git a/configure.in b/configure.in index 511552d713..83eee4d348 100644 --- a/configure.in +++ b/configure.in @@ -226,7 +226,7 @@ dnl ------------------------------------------------------------------- dnl Check for functions before libevent, since libevent-1.2 apparently dnl exports strlcpy without defining it in a header. -AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit strlcat strlcpy strtoull getaddrinfo localtime_r gmtime_r memmem strtok_r flock prctl vasprintf) +AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit strlcat strlcpy strtoull getaddrinfo localtime_r gmtime_r memmem strtok_r flock prctl vasprintf sysconf) using_custom_malloc=no if test x$enable_openbsd_malloc = xyes ; then diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 620f938741..e670bdb6b7 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -397,6 +397,11 @@ Other options can be specified either on the command-line (--option networkstatus. This is an advanced option; you generally shouldn't have to mess with it. (Default: not set.) +**DisableIOCP** **0**|**1**:: + If Tor was built to use the Libevent's "bufferevents" networking code + and you're running on Windows, setting this option to 1 will tell Libevent + not to use the Windows IOCP networking API. (Default: 1) + CLIENT OPTIONS -------------- @@ -855,7 +860,9 @@ is non-zero): characters inclusive, and must contain only the characters [a-zA-Z0-9]. **NumCPUs** __num__:: - How many processes to use at once for decrypting onionskins. (Default: 1) + How many processes to use at once for decrypting onionskins and other + parallelizable operations. If this is set to 0, Tor will try to detect + how many CPUs you have, defaulting to 1 if it can't tell. (Default: 0) **ORPort** __PORT__:: Advertise this port to listen for connections from Tor clients and servers. diff --git a/src/common/compat.c b/src/common/compat.c index 20394b4c5d..b7f4f17cae 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1898,6 +1898,52 @@ spawn_exit(void) #endif } +/** Implementation logic for compute_num_cpus(). */ +static int +compute_num_cpus_impl(void) +{ +#ifdef MS_WINDOWS + SYSTEM_INFO info; + memset(&info, 0, sizeof(info)); + GetSystemInfo(&info); + if (info.dwNumberOfProcessors >= 1 && info.dwNumberOfProcessors < INT_MAX) + return (int)info.dwNumberOfProcessors; + else + return -1; +#elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) + long cpus = sysconf(_SC_NPROCESSORS_CONF); + if (cpus >= 1 && cpus < INT_MAX) + return (int)cpus; + else + return -1; +#else + return -1; +#endif +} + +#define MAX_DETECTABLE_CPUS 16 + +/** Return how many CPUs we are running with. We assume that nobody is + * using hot-swappable CPUs, so we don't recompute this after the first + * time. Return -1 if we don't know how to tell the number of CPUs on this + * system. + */ +int +compute_num_cpus(void) +{ + static int num_cpus = -2; + if (num_cpus == -2) { + num_cpus = compute_num_cpus_impl(); + tor_assert(num_cpus != -2); + if (num_cpus > MAX_DETECTABLE_CPUS) + log_notice(LD_GENERAL, "Wow! I detected that you have %d CPUs. I " + "will not autodetect any more than %d, though. If you " + "want to configure more, set NumCPUs in your torrc", + num_cpus, MAX_DETECTABLE_CPUS); + } + return num_cpus; +} + /** Set *timeval to the current time of day. On error, log and terminate. * (Same as gettimeofday(timeval,NULL), but never returns -1.) */ diff --git a/src/common/compat.h b/src/common/compat.h index 7d59501e2b..5b6cfc40b5 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -522,6 +522,8 @@ void spawn_exit(void) ATTR_NORETURN; #undef TOR_IS_MULTITHREADED #endif +int compute_num_cpus(void); + /* Because we use threads instead of processes on most platforms (Windows, * Linux, etc), we need locking for them. On platforms with poor thread * support or broken gethostbyname_r, these functions are no-ops. */ diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index bbab06e8d4..6afd3c1d29 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -159,7 +159,7 @@ struct event_base *the_event_base = NULL; /** Initialize the Libevent library and set up the event base. */ void -tor_libevent_initialize(void) +tor_libevent_initialize(tor_libevent_cfg *torcfg) { tor_assert(the_event_base == NULL); @@ -171,7 +171,21 @@ tor_libevent_initialize(void) #endif #ifdef HAVE_EVENT2_EVENT_H - the_event_base = event_base_new(); + { + struct event_config *cfg = event_config_new(); + +#if defined(MS_WINDOWS) && defined(USE_BUFFEREVENTS) + if (! torcfg->disable_iocp) + event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP); +#endif + +#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,0,7) + if (torcfg->num_cpus > 0) + event_config_set_num_cpus_hint(cfg, torcfg->num_cpus); +#endif + + the_event_base = event_base_new_with_config(cfg); + } #else the_event_base = event_init(); #endif diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index f483d6ee6d..ecf25806d5 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -56,7 +56,12 @@ struct timeval; int tor_event_base_loopexit(struct event_base *base, struct timeval *tv); #endif -void tor_libevent_initialize(void); +typedef struct tor_libevent_cfg { + int disable_iocp; + int num_cpus; +} tor_libevent_cfg; + +void tor_libevent_initialize(tor_libevent_cfg *cfg); struct event_base *tor_libevent_get_base(void); const char *tor_libevent_get_method(void); void tor_check_libevent_version(const char *m, int server, diff --git a/src/or/config.c b/src/or/config.c index 6d8addeb2d..23cad9268b 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -224,6 +224,7 @@ static config_var_t _option_vars[] = { V(DirReqStatistics, BOOL, "0"), VAR("DirServer", LINELIST, DirServers, NULL), V(DisableAllSwap, BOOL, "0"), + V(DisableIOCP, BOOL, "1"), V(DNSPort, UINT, "0"), V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), @@ -306,7 +307,7 @@ static config_var_t _option_vars[] = { V(WarnUnsafeSocks, BOOL, "1"), V(NoPublish, BOOL, "0"), VAR("NodeFamily", LINELIST, NodeFamilies, NULL), - V(NumCpus, UINT, "1"), + V(NumCpus, UINT, "0"), V(NumEntryGuards, UINT, "3"), V(ORListenAddress, LINELIST, NULL), V(ORPort, UINT, "0"), @@ -554,7 +555,7 @@ static int is_listening_on_low_port(uint16_t port_option, static uint64_t config_parse_memunit(const char *s, int *ok); static int config_parse_interval(const char *s, int *ok); -static void init_libevent(void); +static void init_libevent(const or_options_t *options); static int opt_streq(const char *s1, const char *s2); /** Magic value for or_options_t. */ @@ -955,7 +956,7 @@ options_act_reversible(or_options_t *old_options, char **msg) /* Set up libevent. (We need to do this before we can register the * listeners as listeners.) */ if (running_tor && !libevent_initialized) { - init_libevent(); + init_libevent(options); libevent_initialized = 1; } @@ -4891,13 +4892,29 @@ config_parse_interval(const char *s, int *ok) return (int)r; } +/** Return the number of cpus configured in options. If we are + * told to auto-detect the number of cpus, return the auto-detected number. */ +int +get_num_cpus(const or_options_t *options) +{ + if (options->NumCpus == 0) { + int n = compute_num_cpus(); + return (n >= 1) ? n : 1; + } else { + return options->NumCpus; + } +} + /** * Initialize the libevent library. */ static void -init_libevent(void) +init_libevent(const or_options_t *options) { const char *badness=NULL; + tor_libevent_cfg cfg; + + tor_assert(options); configure_libevent_logging(); /* If the kernel complains that some method (say, epoll) doesn't @@ -4907,7 +4924,11 @@ init_libevent(void) tor_check_libevent_header_compatibility(); - tor_libevent_initialize(); + memset(&cfg, 0, sizeof(cfg)); + cfg.disable_iocp = options->DisableIOCP; + cfg.num_cpus = get_num_cpus(options); + + tor_libevent_initialize(&cfg); suppress_libevent_log_msg(NULL); diff --git a/src/or/config.h b/src/or/config.h index 7a4ba5c60f..bd5827b4e8 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -57,6 +57,8 @@ char *options_get_datadir_fname2_suffix(or_options_t *options, #define get_datadir_fname_suffix(sub1, suffix) \ get_datadir_fname2_suffix((sub1), NULL, (suffix)) +int get_num_cpus(const or_options_t *options); + or_state_t *get_or_state(void); int or_state_save(time_t now); diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index e5b2c71025..cfe9f3af9c 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -366,7 +366,7 @@ spawn_cpuworker(void) static void spawn_enough_cpuworkers(void) { - int num_cpuworkers_needed = get_options()->NumCpus; + int num_cpuworkers_needed = get_num_cpus(get_options()); if (num_cpuworkers_needed < MIN_CPUWORKERS) num_cpuworkers_needed = MIN_CPUWORKERS; diff --git a/src/or/or.h b/src/or/or.h index 4741cc341b..d2a7714db3 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2892,6 +2892,10 @@ typedef struct { */ double CircuitPriorityHalflife; + /** If true, do not enable IOCP on windows with bufferevents, even if + * we think we could. */ + int DisableIOCP; + } or_options_t; /** Persistent state for an onion router, as saved to disk. */