/* Copyright (c) 2006, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * --- * Author: Sanjay Ghemawat */ #include "config.h" #include /* For nanosleep() */ #include /* For sched_yield() */ #ifdef HAVE_UNISTD_H #include /* For nanosleep() on Windows, read() */ #endif #include /* for open(), O_RDONLY */ #include /* for strncmp */ #include #include "base/spinlock.h" //#include "base/sysinfo.h" /* for NumCPUs() */ // We can do contention-profiling of SpinLocks, but the code is in // mutex.cc, which is not always linked in with spinlock. Hence we // provide this weak definition, which is used if mutex.cc isn't linked in. ATTRIBUTE_WEAK extern void SubmitSpinLockProfileData(const void *, int64); void SubmitSpinLockProfileData(const void *, int64) {} static int adaptive_spin_count = 0; const base::LinkerInitialized SpinLock::LINKER_INITIALIZED = base::LINKER_INITIALIZED; struct SpinLock_InitHelper { SpinLock_InitHelper() { // On multi-cpu machines, spin for longer before yielding // the processor or sleeping. Reduces idle time significantly. //if (NumCPUs() > 1) { // adaptive_spin_count = 1000; // } } }; // Hook into global constructor execution: // We do not do adaptive spinning before that, // but nothing lock-intensive should be going on at that time. static SpinLock_InitHelper init_helper; void SpinLock::SlowLock() { int saved_errno = errno; // save and restore errno for signal safety int c = adaptive_spin_count; // Spin a few times in the hope that the lock holder releases the lock while ((c > 0) && (lockword_ != 0)) { c--; } if (lockword_ == 1) { sched_yield(); // Spinning failed. Let's try to be gentle. } while (Acquire_CompareAndSwap(&lockword_, 0, 1) != 0) { // This code was adapted from the ptmalloc2 implementation of // spinlocks which would sched_yield() upto 50 times before // sleeping once for a few milliseconds. Mike Burrows suggested // just doing one sched_yield() outside the loop and always // sleeping after that. This change helped a great deal on the // performance of spinlocks under high contention. A test program // with 10 threads on a dual Xeon (four virtual processors) went // from taking 30 seconds to 16 seconds. // Sleep for a few milliseconds struct timespec tm; tm.tv_sec = 0; tm.tv_nsec = 2000001; nanosleep(&tm, NULL); } errno = saved_errno; }