1/* Copyright (c) 2009, Google Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * ---
31 * This file is a Linux-specific part of spinlock_internal.cc
32 */
33
34#include <errno.h>
35#include <sched.h>
36#include <time.h>
37#include <limits.h>
38#include "base/linux_syscall_support.h"
39
40#define FUTEX_WAIT 0
41#define FUTEX_WAKE 1
42#define FUTEX_PRIVATE_FLAG 128
43
44// Note: Instead of making direct system calls that are inlined, we rely
45//       on the syscall() function in glibc to do the right thing. This
46//       is necessary to make the code compatible with the seccomp sandbox,
47//       which needs to be able to find and patch all places where system
48//       calls are made. Scanning through and patching glibc is fast, but
49//       doing so on the entire Chrome binary would be prohibitively
50//       expensive.
51//       This is a notable change from the upstream version of tcmalloc,
52//       which prefers direct system calls in order to improve compatibility
53//       with older toolchains and runtime libraries.
54
55static bool have_futex;
56static int futex_private_flag = FUTEX_PRIVATE_FLAG;
57
58namespace {
59static struct InitModule {
60  InitModule() {
61    int x = 0;
62    // futexes are ints, so we can use them only when
63    // that's the same size as the lockword_ in SpinLock.
64#ifdef __arm__
65    // ARM linux doesn't support sys_futex1(void*, int, int, struct timespec*);
66    have_futex = 0;
67#else
68    have_futex = (sizeof (Atomic32) == sizeof (int) &&
69                  syscall(__NR_futex, &x, FUTEX_WAKE, 1, 0) >= 0);
70#endif
71    if (have_futex &&
72        syscall(__NR_futex, &x, FUTEX_WAKE | futex_private_flag, 1, 0) < 0) {
73      futex_private_flag = 0;
74    }
75  }
76} init_module;
77
78}  // anonymous namespace
79
80
81namespace base {
82namespace internal {
83
84void SpinLockDelay(volatile Atomic32 *w, int32 value, int loop) {
85  if (loop != 0) {
86    int save_errno = errno;
87    struct timespec tm;
88    tm.tv_sec = 0;
89    if (have_futex) {
90      // Wait between 0-16ms.
91      tm.tv_nsec = base::internal::SuggestedDelayNS(loop);
92      // Note: since Unlock() is optimized to not do a compare-and-swap,
93      // we can't expect explicit wake-ups. Therefore we shouldn't wait too
94      // long here.
95      syscall(__NR_futex, reinterpret_cast<int *>(const_cast<Atomic32 *>(w)),
96                FUTEX_WAIT | futex_private_flag,
97                value, reinterpret_cast<struct kernel_timespec *>(&tm));
98    } else {
99      tm.tv_nsec = 2000001;   // above 2ms so linux 2.4 doesn't spin
100      nanosleep(&tm, NULL);
101    }
102    errno = save_errno;
103  }
104}
105
106void SpinLockWake(volatile Atomic32 *w, bool all) {
107  if (have_futex) {
108    syscall(__NR_futex, reinterpret_cast<int *>(const_cast<Atomic32 *>(w)),
109              FUTEX_WAKE | futex_private_flag, 1, 0);
110  }
111}
112
113} // namespace internal
114} // namespace base
115