00001 /* Copyright (c) 2006, Google Inc. 00002 * All rights reserved. 00003 * 00004 * Redistribution and use in source and binary forms, with or without 00005 * modification, are permitted provided that the following conditions are 00006 * met: 00007 * 00008 * * Redistributions of source code must retain the above copyright 00009 * notice, this list of conditions and the following disclaimer. 00010 * * Redistributions in binary form must reproduce the above 00011 * copyright notice, this list of conditions and the following disclaimer 00012 * in the documentation and/or other materials provided with the 00013 * distribution. 00014 * * Neither the name of Google Inc. nor the names of its 00015 * contributors may be used to endorse or promote products derived from 00016 * this software without specific prior written permission. 00017 * 00018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00019 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00020 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 00021 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 00022 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00023 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00024 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00025 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00026 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00028 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00029 * 00030 * --- 00031 * Author: Sanjay Ghemawat 00032 */ 00033 00034 #include "config.h" 00035 #include <time.h> /* For nanosleep() */ 00036 #include <sched.h> /* For sched_yield() */ 00037 #ifdef HAVE_UNISTD_H 00038 #include <unistd.h> /* For nanosleep() on Windows, read() */ 00039 #endif 00040 #include <fcntl.h> /* for open(), O_RDONLY */ 00041 #include <string.h> /* for strncmp */ 00042 #include <errno.h> 00043 #include "base/spinlock.h" 00044 00045 static int adaptive_spin_count = 0; 00046 static int num_cpus = -1; 00047 00048 // It's important this not call malloc! -- it is called at global-construct 00049 // time, before we've set up all our proper malloc hooks and such. 00050 static int NumCPUs() { 00051 if (num_cpus >= 0) 00052 return num_cpus; // value is cached 00053 00054 const char* pname = "/proc/cpuinfo"; 00055 int fd = open(pname, O_RDONLY); 00056 if (fd == -1) { 00057 num_cpus = 1; // conservative assumption; TODO: do better 00058 return num_cpus; 00059 } 00060 char line[1024]; 00061 int chars_read; 00062 num_cpus = 0; 00063 do { // a line at a time 00064 chars_read = 0; 00065 char* linepos = line; 00066 while (linepos - line < sizeof(line)-1 && 00067 (chars_read=read(fd, linepos, 1)) == 1 && 00068 *linepos != '\n') // read one line 00069 linepos++; 00070 *linepos = '\0'; // terminate the line at the \n 00071 if (strncmp(line, "processor ", sizeof("processor ")-1) == 0) 00072 num_cpus++; 00073 } while (chars_read > 0); // stop when we get to EOF 00074 close(fd); 00075 return num_cpus == 0 ? 1 : num_cpus; 00076 } 00077 00078 struct SpinLock_InitHelper { 00079 SpinLock_InitHelper() { 00080 // On multi-cpu machines, spin for longer before yielding 00081 // the processor or sleeping. Reduces idle time significantly. 00082 if (NumCPUs() > 1) { 00083 adaptive_spin_count = 1000; 00084 } 00085 } 00086 }; 00087 00088 // Hook into global constructor execution: 00089 // We do not do adaptive spinning before that, 00090 // but nothing lock-intensive should be going on at that time. 00091 static SpinLock_InitHelper init_helper; 00092 00093 void SpinLock::SlowLock() { 00094 int saved_errno = errno; // save and restore errno for signal safety 00095 int c = adaptive_spin_count; 00096 00097 // Spin a few times in the hope that the lock holder releases the lock 00098 while ((c > 0) && (lockword_ != 0)) { 00099 c--; 00100 } 00101 00102 if (lockword_ == 1) { 00103 sched_yield(); // Spinning failed. Let's try to be gentle. 00104 } 00105 00106 while (Acquire_CompareAndSwap(&lockword_, 0, 1) != 0) { 00107 // This code was adapted from the ptmalloc2 implementation of 00108 // spinlocks which would sched_yield() upto 50 times before 00109 // sleeping once for a few milliseconds. Mike Burrows suggested 00110 // just doing one sched_yield() outside the loop and always 00111 // sleeping after that. This change helped a great deal on the 00112 // performance of spinlocks under high contention. A test program 00113 // with 10 threads on a dual Xeon (four virtual processors) went 00114 // from taking 30 seconds to 16 seconds. 00115 00116 // Sleep for a few milliseconds 00117 struct timespec tm; 00118 tm.tv_sec = 0; 00119 tm.tv_nsec = 2000001; 00120 nanosleep(&tm, NULL); 00121 } 00122 errno = saved_errno; 00123 }