Added cpu_affinity_map configuration option to bind workers to CPU cores.

Allows the admin to place all workers on individual cores without writing a
lot of if-statements. For example,

    cpu_affinity_map process_numbers=1,2,3,4 cores=1,3,5,7

will have an effect for kids 1 through 4 only and will place them on even
cores starting with core #1.

If there are conflicts for a given process, the latest option wins and a
warning is printed. If the number of specified processes do not match the
number of specified cores, Squid quits with an error. Multiple
cpu_affinity_map options are merged.

Squid builds on systems without Linux CPU affinity calls, but setting affinity
only works if there are sched_getaffinity(2) and sched_setaffinity(2)
available. If there is no OS support but cpu_affinity options are configured,
Squid quits with an error. If there is OS support but calls fail, Squid prints
an error but does not quit.

=== modified file 'compat/Makefile.am'
--- compat/Makefile.am	2010-08-20 16:15:46 +0000
+++ compat/Makefile.am	2010-09-23 16:57:21 +0000
@@ -14,6 +14,7 @@
 	assert.h \
 	compat.h \
 	compat_shared.h \
+	cpu.h \
 	debug.h \
 	drand48.h \
 	eui64_aton.h \

=== added file 'compat/cpu.h'
--- compat/cpu.h	1970-01-01 00:00:00 +0000
+++ compat/cpu.h	2010-09-23 16:58:11 +0000
@@ -0,0 +1,75 @@
+/*
+ * $Id$
+ */
+#ifndef SQUID_COMPAT_CPU_H
+#define SQUID_COMPAT_CPU_H
+
+#ifndef SQUID_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_CPU_AFFINITY
+#if HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+// glibc prior to 2.6 lacks CPU_COUNT
+#ifndef CPU_COUNT
+#define CPU_COUNT(set) CpuCount(set)
+/// CPU_COUNT replacement
+inline int
+CpuCount(const cpu_set_t *set)
+{
+    int count = 0;
+    for (int i = 0; i < CPU_SETSIZE; ++i) {
+        if (CPU_ISSET(i, set))
+            ++count;
+    }
+    return count;
+}
+#endif /* CPU_COUNT */
+
+// glibc prior to 2.7 lacks CPU_AND
+#ifndef CPU_AND
+#define CPU_AND(destset, srcset1, srcset2) CpuAnd((destset), (srcset1), (srcset2))
+/// CPU_AND replacement
+inline void
+CpuAnd(cpu_set_t *destset, const cpu_set_t *srcset1, const cpu_set_t *srcset2)
+{
+    CPU_ZERO(destset);
+    for (int i = 0; i < CPU_SETSIZE; ++i) {
+        if (CPU_ISSET(i, srcset1) && CPU_ISSET(i, srcset2))
+            CPU_SET(i, destset);
+    }
+}
+#endif /* CPU_AND */
+
+#else /* HAVE_CPU_AFFINITY */
+
+#if HAVE_ERRNO_H
+#include <errno.h> /* for ENOTSUP */
+#endif
+
+/* failing replacements to minimize the number of if-HAVE_CPU_AFFINITYs */
+typedef struct
+{
+    int bits;
+} cpu_set_t;
+#define CPU_SETSIZE 0
+#define CPU_COUNT(set) 0
+#define CPU_AND(destset, srcset1, srcset2) (void)0
+#define CPU_ZERO(set) (void)0
+inline int
+sched_setaffinity(int, size_t, cpu_set_t *)
+{
+    return ENOTSUP;
+}
+inline int
+sched_getaffinity(int, size_t, cpu_set_t *)
+{
+    return ENOTSUP;
+}
+
+#endif /* HAVE_CPU_AFFINITY */
+
+#endif /* SQUID_COMPAT_CPU_H */

=== modified file 'configure.in'
--- configure.in	2010-09-10 15:41:41 +0000
+++ configure.in	2010-09-23 00:07:52 +0000
@@ -2934,6 +2934,8 @@
 	__res_init \
 	rint \
 	sbrk \
+	sched_getaffinity \
+	sched_setaffinity \
 	select \
 	seteuid \
 	setgroups \
@@ -3008,6 +3010,10 @@
   select) AC_DEFINE(USE_SELECT,1,[Use select() for the IO loop]) ;;
 esac
 
+if test "x$ac_cv_func_sched_getaffinity" = "xyes" -a "x$ac_cv_func_sched_setaffinity" = "xyes" ; then
+  AC_DEFINE(HAVE_CPU_AFFINITY,1,[Support setting CPU affinity for workers])
+fi
+
 SQUID_CHECK_SETRESUID_WORKS
 if test "x$squid_cv_resuid_works" = "xyes" ; then
   AC_DEFINE(HAVE_SETRESUID,1,[Yay! Another Linux brokenness. Knowing that setresuid() exists is not enough, because RedHat 5.0 declares setresuid() but does not implement it.])

=== added file 'src/CpuAffinity.cc'
--- src/CpuAffinity.cc	1970-01-01 00:00:00 +0000
+++ src/CpuAffinity.cc	2010-09-23 00:07:52 +0000
@@ -0,0 +1,60 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "base/TextException.h"
+#include "CpuAffinity.h"
+#include "CpuAffinityMap.h"
+#include "CpuAffinitySet.h"
+#include "structs.h"
+
+#include <algorithm>
+
+static CpuAffinitySet *TheCpuAffinitySet = NULL;
+
+
+void
+CpuAffinityInit()
+{
+    Must(!TheCpuAffinitySet);
+    if (Config.cpuAffinityMap) {
+        const int processNumber = InDaemonMode() ? KidIdentifier : 1;
+        TheCpuAffinitySet = Config.cpuAffinityMap->calculateSet(processNumber);
+        if (TheCpuAffinitySet)
+            TheCpuAffinitySet->apply();
+    }
+}
+
+void
+CpuAffinityReconfigure()
+{
+    if (TheCpuAffinitySet) {
+        TheCpuAffinitySet->undo();
+        delete TheCpuAffinitySet;
+        TheCpuAffinitySet = NULL;
+    }
+    CpuAffinityInit();
+}
+
+void
+CpuAffinityCheck()
+{
+    if (Config.cpuAffinityMap) {
+        Must(!Config.cpuAffinityMap->processes().empty());
+        const int maxProcess =
+            *std::max_element(Config.cpuAffinityMap->processes().begin(),
+                              Config.cpuAffinityMap->processes().end());
+
+        // in no-deamon mode, there is one process regardless of squid.conf
+        const int numberOfProcesses = InDaemonMode() ? NumberOfKids() : 1;
+
+        if (maxProcess > numberOfProcesses) {
+            debugs(54, DBG_IMPORTANT, "WARNING: 'cpu_affinity_map' has "
+                   "non-existing process number(s)");
+        }
+    }
+}

=== added file 'src/CpuAffinity.h'
--- src/CpuAffinity.h	1970-01-01 00:00:00 +0000
+++ src/CpuAffinity.h	2010-09-23 05:01:41 +0000
@@ -0,0 +1,21 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_CPU_AFFINITY_H
+#define SQUID_CPU_AFFINITY_H
+
+#include "config.h"
+
+/// set CPU affinity for this process on startup
+SQUIDCEXTERN void CpuAffinityInit();
+
+/// reconfigure CPU affinity for this process
+SQUIDCEXTERN void CpuAffinityReconfigure();
+
+/// check CPU affinity configuration and print warnings if needed
+SQUIDCEXTERN void CpuAffinityCheck();
+
+
+#endif // SQUID_CPU_AFFINITY_H

=== added file 'src/CpuAffinityMap.cc'
--- src/CpuAffinityMap.cc	1970-01-01 00:00:00 +0000
+++ src/CpuAffinityMap.cc	2010-09-23 05:05:02 +0000
@@ -0,0 +1,59 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "base/TextException.h"
+#include "CpuAffinityMap.h"
+#include "CpuAffinitySet.h"
+#include "Debug.h"
+
+
+bool
+CpuAffinityMap::add(const Vector<int> &aProcesses, const Vector<int> &aCores)
+{
+    if (aProcesses.size() != aCores.size())
+        return false;
+
+    for (size_t i = 0; i < aProcesses.size(); ++i) {
+        const int process = aProcesses[i];
+        const int core = aCores[i];
+        if (process <= 0 || core <= 0)
+            return false;
+        theProcesses.push_back(process);
+        theCores.push_back(core);
+    }
+
+    return true;
+}
+
+CpuAffinitySet *
+CpuAffinityMap::calculateSet(const int targetProcess) const
+{
+    Must(theProcesses.size() == theCores.size());
+    int core = 0;
+    for (size_t i = 0; i < theProcesses.size(); ++i) {
+        const int process = theProcesses[i];
+        if (process == targetProcess)
+        {
+            if (core > 0) {
+                debugs(54, DBG_CRITICAL, "WARNING: conflicting "
+                       "'cpu_affinity_map' for process number " << process <<
+                       ", using the last core seen: " << theCores[i]);
+            }
+            core = theCores[i];
+        }
+    }
+    CpuAffinitySet *cpuAffinitySet = NULL;
+    if (core > 0) {
+        cpuAffinitySet = new CpuAffinitySet;
+        cpu_set_t cpuSet;
+        CPU_ZERO(&cpuSet);
+        CPU_SET(core - 1, &cpuSet);
+        cpuAffinitySet->set(cpuSet);
+    }
+    return cpuAffinitySet;
+}

=== added file 'src/CpuAffinityMap.h'
--- src/CpuAffinityMap.h	1970-01-01 00:00:00 +0000
+++ src/CpuAffinityMap.h	2010-09-23 00:07:52 +0000
@@ -0,0 +1,35 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_CPU_AFFINITY_MAP_H
+#define SQUID_CPU_AFFINITY_MAP_H
+
+#include "Array.h"
+
+class CpuAffinitySet;
+
+
+/// stores cpu_affinity_map configuration
+class CpuAffinityMap
+{
+public:
+    /// append cpu_affinity_map option
+    bool add(const Vector<int> &aProcesses, const Vector<int> &aCores);
+
+    /// calculate CPU set for this process
+    CpuAffinitySet *calculateSet(const int targetProcess) const;
+
+    /// returns list of process numbers
+    const Vector<int> &processes() const { return theProcesses; }
+
+    /// returns list of cores
+    const Vector<int> &cores() const { return theCores; }
+
+private:
+    Vector<int> theProcesses; ///< list of process numbers
+    Vector<int> theCores; ///< list of cores
+};
+
+#endif // SQUID_CPU_AFFINITY_MAP_H

=== added file 'src/CpuAffinitySet.cc'
--- src/CpuAffinitySet.cc	1970-01-01 00:00:00 +0000
+++ src/CpuAffinitySet.cc	2010-09-23 04:54:31 +0000
@@ -0,0 +1,77 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "base/TextException.h"
+#include "CpuAffinitySet.h"
+#include "Debug.h"
+#include "util.h"
+
+#if HAVE_STRING_H
+#include <string.h> /* for memcpy() */
+#endif
+
+
+CpuAffinitySet::CpuAffinitySet()
+{
+    CPU_ZERO(&theCpuSet);
+    CPU_ZERO(&theOrigCpuSet);
+}
+
+void
+CpuAffinitySet::apply()
+{
+    Must(CPU_COUNT(&theCpuSet) > 0); // CPU affinity mask set
+    Must(!applied());
+
+    bool success = false;
+    if (sched_getaffinity(0, sizeof(theOrigCpuSet), &theOrigCpuSet)) {
+        debugs(54, DBG_IMPORTANT, "ERROR: failed to get CPU affinity for "
+               "process PID " << getpid() << ", ignoring CPU affinity for "
+               "this process: " << xstrerror());
+    } else {
+        cpu_set_t cpuSet;
+        xmemcpy(&cpuSet, &theCpuSet, sizeof(cpuSet));
+        CPU_AND(&cpuSet, &cpuSet, &theOrigCpuSet);
+        if (CPU_COUNT(&cpuSet) <= 0) {
+            debugs(54, DBG_IMPORTANT, "ERROR: invalid CPU affinity for process "
+                   "PID " << getpid() << ", may be caused by an invalid core in "
+                   "'cpu_affinity_map' or by external affinity restrictions");
+        } else if (sched_setaffinity(0, sizeof(cpuSet), &cpuSet)) {
+            debugs(54, DBG_IMPORTANT, "ERROR: failed to set CPU affinity for "
+                   "process PID " << getpid() << ": " << xstrerror());
+        } else
+            success = true;
+    }
+    if (!success)
+        CPU_ZERO(&theOrigCpuSet);
+}
+
+void
+CpuAffinitySet::undo()
+{
+    if (applied()) {
+        if (sched_setaffinity(0, sizeof(theOrigCpuSet), &theOrigCpuSet)) {
+            debugs(54, DBG_IMPORTANT, "ERROR: failed to restore original CPU "
+                   "affinity for process PID " << getpid() << ": " <<
+                   xstrerror());
+        }
+        CPU_ZERO(&theOrigCpuSet);
+    }
+}
+
+bool
+CpuAffinitySet::applied() const
+{
+    return (CPU_COUNT(&theOrigCpuSet) > 0);
+}
+
+void
+CpuAffinitySet::set(const cpu_set_t &aCpuSet)
+{
+    xmemcpy(&theCpuSet, &aCpuSet, sizeof(theCpuSet));
+}

=== added file 'src/CpuAffinitySet.h'
--- src/CpuAffinitySet.h	1970-01-01 00:00:00 +0000
+++ src/CpuAffinitySet.h	2010-09-23 05:03:16 +0000
@@ -0,0 +1,35 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_CPU_AFFINITY_SET_H
+#define SQUID_CPU_AFFINITY_SET_H
+
+#include "config.h"
+#include "compat/cpu.h"
+
+/// cpu affinity management for a single process
+class CpuAffinitySet
+{
+public:
+    CpuAffinitySet();
+
+    /// set CPU affinity for this process
+    void apply();
+
+    /// undo CPU affinity changes for this process
+    void undo();
+
+    /// whether apply() was called and was not undone
+    bool applied() const;
+
+    /// set CPU affinity mask
+    void set(const cpu_set_t &aCpuSet);
+
+private:
+    cpu_set_t theCpuSet; ///< configured CPU affinity for this process
+    cpu_set_t theOrigCpuSet; ///< CPU affinity for this process before apply()
+};
+
+#endif // SQUID_CPU_AFFINITY_SET_H

=== modified file 'src/Makefile.am'
--- src/Makefile.am	2010-09-11 00:52:48 +0000
+++ src/Makefile.am	2010-09-23 00:07:52 +0000
@@ -291,6 +291,12 @@
 	ConfigParser.cc \
 	ConfigParser.h \
 	ConnectionDetail.h \
+	CpuAffinity.cc \
+	CpuAffinity.h \
+	CpuAffinityMap.cc \
+	CpuAffinityMap.h \
+	CpuAffinitySet.cc \
+	CpuAffinitySet.h \
 	debug.cc \
 	Debug.h \
 	defines.h \
@@ -1135,6 +1141,10 @@
 	$(squid_COMMSOURCES) \
 	ConfigOption.cc \
 	ConfigParser.cc \
+	CpuAffinityMap.cc \
+	CpuAffinityMap.h \
+	CpuAffinitySet.cc \
+	CpuAffinitySet.h \
 	$(DELAY_POOL_SOURCE) \
 	disk.cc \
 	dlink.h \
@@ -1320,6 +1330,10 @@
 	$(squid_COMMSOURCES) \
 	ConfigOption.cc \
 	ConfigParser.cc \
+	CpuAffinityMap.cc \
+	CpuAffinityMap.h \
+	CpuAffinitySet.cc \
+	CpuAffinitySet.h \
 	$(DELAY_POOL_SOURCE) \
 	disk.cc \
 	dlink.h \
@@ -1476,6 +1490,10 @@
 	$(squid_COMMSOURCES) \
 	ConfigOption.cc \
 	ConfigParser.cc \
+	CpuAffinityMap.cc \
+	CpuAffinityMap.h \
+	CpuAffinitySet.cc \
+	CpuAffinitySet.h \
 	$(DELAY_POOL_SOURCE) \
 	disk.cc \
 	dlink.h \
@@ -1619,6 +1637,10 @@
 	$(squid_COMMSOURCES) \
 	ConfigOption.cc \
 	ConfigParser.cc \
+	CpuAffinityMap.cc \
+	CpuAffinityMap.h \
+	CpuAffinitySet.cc \
+	CpuAffinitySet.h \
 	tests/stub_main_cc.cc \
 	debug.cc \
 	$(DELAY_POOL_SOURCE) \
@@ -1782,6 +1804,10 @@
 	$(squid_COMMSOURCES) \
 	ConfigOption.cc \
 	ConfigParser.cc \
+	CpuAffinityMap.cc \
+	CpuAffinityMap.h \
+	CpuAffinitySet.cc \
+	CpuAffinitySet.h \
 	$(DELAY_POOL_SOURCE) \
 	disk.cc \
 	dlink.h \
@@ -2158,6 +2184,10 @@
 	$(squid_COMMSOURCES) \
 	ConfigOption.cc \
 	ConfigParser.cc \
+	CpuAffinityMap.cc \
+	CpuAffinityMap.h \
+	CpuAffinitySet.cc \
+	CpuAffinitySet.h \
 	$(DELAY_POOL_SOURCE) \
 	disk.cc \
 	dlink.h \

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2010-08-25 03:08:37 +0000
+++ src/cache_cf.cc	2010-09-23 05:06:29 +0000
@@ -50,6 +50,7 @@
 #include "auth/Scheme.h"
 #include "CacheManager.h"
 #include "ConfigParser.h"
+#include "CpuAffinityMap.h"
 #include "eui/Config.h"
 #if USE_SQUID_ESI
 #include "esi/Parser.h"
@@ -181,6 +182,12 @@
 static void parse_b_size_t(size_t * var);
 static void parse_b_int64_t(int64_t * var);
 
+static bool parseNamedIntList(const char *data, const String &name, Vector<int> &list);
+
+static void parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
+static void dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap);
+static void free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
+
 static int parseOneConfigFile(const char *file_name, unsigned int depth);
 
 /*
@@ -3842,6 +3849,80 @@
     }
 }
 
+/// parses list of integers form name=N1,N2,N3,...
+static bool
+parseNamedIntList(const char *data, const String &name, Vector<int> &list)
+{
+    if (data && (strncmp(data, name.rawBuf(), name.size()) == 0)) {
+        data += name.size();
+        if (*data == '=') {
+            while (true) {
+                ++data;
+                int value = 0;
+                if (!StringToInt(data, value, &data, 10))
+                    break;
+                list.push_back(value);
+                if (*data == '\0' || *data != ',')
+                    break;
+            }
+        }
+    }
+    return *data == '\0';
+}
+
+static void
+parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap) {
+#if !HAVE_CPU_AFFINITY
+    debugs(3, DBG_CRITICAL, "FATAL: Squid built with no CPU affinity " <<
+           "support, do not set 'cpu_affinity_map'");
+    self_destruct();
+#endif /* HAVE_CPU_AFFINITY */
+
+    if (!*cpuAffinityMap)
+        *cpuAffinityMap = new CpuAffinityMap;
+
+    const char *const pToken = strtok(NULL, w_space);
+    const char *const cToken = strtok(NULL, w_space);
+    Vector<int> processes, cores;
+    if (!parseNamedIntList(pToken, "process_numbers", processes)) {
+        debugs(3, DBG_CRITICAL, "FATAL: bad 'process_numbers' parameter " <<
+               "in 'cpu_affinity_map'");
+        self_destruct();
+    } else if (!parseNamedIntList(cToken, "cores", cores)) {
+        debugs(3, DBG_CRITICAL, "FATAL: bad 'cores' parameter in " <<
+               "'cpu_affinity_map'");
+        self_destruct();
+    } else if (!(*cpuAffinityMap)->add(processes, cores)) {
+        debugs(3, DBG_CRITICAL, "FATAL: bad 'cpu_affinity_map'; " <<
+               "process_numbers and cores lists differ in length or " <<
+               "contain numbers <= 0");
+        self_destruct();
+    }
+}
+
+static void
+dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap) {
+    if (cpuAffinityMap) {
+        storeAppendPrintf(entry, "%s process_numbers=", name);
+        for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
+            storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
+                              cpuAffinityMap->processes()[i]);
+        }
+        storeAppendPrintf(entry, " cores=");
+        for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
+            storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
+                              cpuAffinityMap->cores()[i]);
+        }
+        storeAppendPrintf(entry, "\n");
+    }
+}
+
+static void
+free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap) {
+    delete *cpuAffinityMap;
+    *cpuAffinityMap = NULL;
+}
+
 #if USE_ADAPTATION
 
 static void

=== modified file 'src/cf.data.depend'
--- src/cf.data.depend	2010-04-05 10:29:50 +0000
+++ src/cf.data.depend	2010-09-23 00:07:52 +0000
@@ -11,6 +11,7 @@
 b_size_t
 cachedir		cache_replacement_policy
 cachemgrpasswd
+CpuAffinityMap
 debug
 delay_pool_access	acl	delay_class
 delay_pool_class	delay_pools

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2010-09-16 21:43:19 +0000
+++ src/cf.data.pre	2010-09-23 00:07:52 +0000
@@ -7072,4 +7072,26 @@
 	does (e.g., listen on http_port and forward HTTP requests).
 DOC_END
 
+NAME: cpu_affinity_map
+TYPE: CpuAffinityMap
+LOC: Config.cpuAffinityMap
+DEFAULT: none
+DOC_START
+	Usage: cpu_affinity_map process_numbers=P1,P2,... cores=C1,C2,...
+
+	Sets 1:1 mapping between Squid processes and CPU cores. For example,
+
+	    cpu_affinity_map process_numbers=1,2,3,4 cores=1,3,5,7
+
+	affects processes 1 through 4 only and places them on the first
+	four even cores, starting with core #1.
+
+	CPU cores are numbered starting from 1. Requires support for
+	sched_getaffinity(2) and sched_setaffinity(2) system calls.
+
+	Multiple cpu_affinity_map options are merged.
+
+	See also: workers
+DOC_END
+
 EOF

=== modified file 'src/main.cc'
--- src/main.cc	2010-09-08 14:46:42 +0000
+++ src/main.cc	2010-09-23 00:07:52 +0000
@@ -40,6 +40,7 @@
 #include "auth/Gadgets.h"
 #include "base/TextException.h"
 #include "ConfigParser.h"
+#include "CpuAffinity.h"
 #include "errorpage.h"
 #include "event.h"
 #include "EventLoop.h"
@@ -770,6 +771,10 @@
         Config.workers = oldWorkers;
     }
 
+    if (IamPrimaryProcess())
+        CpuAffinityCheck();
+    CpuAffinityReconfigure();
+
     setUmask(Config.umask);
     Mem::Report();
     setEffectiveUser();
@@ -1410,6 +1415,10 @@
         return 0;
     }
 
+    if (IamPrimaryProcess())
+        CpuAffinityCheck();
+    CpuAffinityInit();
+
     if (!opt_no_daemon && Config.workers > 0)
         watch_child(argv);
 

=== modified file 'src/protos.h'
--- src/protos.h	2010-08-24 10:35:03 +0000
+++ src/protos.h	2010-09-23 00:07:52 +0000
@@ -583,8 +583,12 @@
 SQUIDCEXTERN bool IamCoordinatorProcess();
 /// whether the current process handles HTTP transactions and such
 SQUIDCEXTERN bool IamWorkerProcess();
+/// Whether we are running in daemon mode
+SQUIDCEXTERN bool InDaemonMode(); // try using specific Iam*() checks above first
 /// Whether there should be more than one worker process running
 SQUIDCEXTERN bool UsingSmp(); // try using specific Iam*() checks above first
+/// number of Kid processes as defined in src/ipc/Kid.h
+SQUIDCEXTERN int NumberOfKids();
 SQUIDCEXTERN int DebugSignal;
 
 /* AYJ debugs function to show locations being reset with memset() */

=== modified file 'src/structs.h'
--- src/structs.h	2010-09-14 07:45:30 +0000
+++ src/structs.h	2010-09-23 00:07:52 +0000
@@ -130,6 +130,7 @@
 
 /* forward decl for SquidConfig, see RemovalPolicy.h */
 
+class CpuAffinityMap;
 class RemovalPolicySettings;
 class external_acl;
 class Store;
@@ -611,6 +612,7 @@
     int umask;
     int max_filedescriptors;
     int workers;
+    CpuAffinityMap *cpuAffinityMap;
 
 #if USE_LOADABLE_MODULES
     wordlist *loadable_module_names;

=== modified file 'src/tools.cc'
--- src/tools.cc	2010-08-09 11:06:36 +0000
+++ src/tools.cc	2010-09-23 00:07:52 +0000
@@ -824,6 +824,12 @@
 }
 
 bool
+InDaemonMode()
+{
+    return !opt_no_daemon && Config.workers > 0;
+}
+
+bool
 UsingSmp()
 {
     return !opt_no_daemon && Config.workers > 1;
@@ -851,6 +857,20 @@
     return IamCoordinatorProcess();
 }
 
+int
+NumberOfKids()
+{
+    // no kids in no-daemon mode
+    if (!InDaemonMode())
+        return 0;
+
+    // workers + the coordinator process
+    if (UsingSmp())
+        return Config.workers + 1;
+
+    return Config.workers;
+}
+
 void
 writePidFile(void)
 {


