1/* Copyright (c) 2008-2010, 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 *     * Neither the name of Google Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27// This file is part of ThreadSanitizer, a dynamic data race detector.
28// Author: Konstantin Serebryany.
29// Author: Timur Iskhodzhanov.
30//
31// See ts_util.h for mode details.
32
33#include "common_util.h"
34#include "thread_sanitizer.h"
35#include "ts_stats.h"
36#include "ts_lock.h"
37#include <stdarg.h>
38
39FLAGS *G_flags = NULL;
40
41#if defined(_MSC_VER)
42
43#pragma comment(lib, "winmm.lib")
44
45# ifdef TS_PIN
46#  include "pin.H"
47# endif
48namespace WINDOWS
49{
50// This is the way of including winows.h recommended by PIN docs.
51#include<Windows.h>
52}
53int getpid() { return WINDOWS::GetCurrentProcessId(); }
54#endif
55
56#if defined(TS_VALGRIND)
57size_t TimeInMilliSeconds() {
58  return VG_(read_millisecond_timer)();
59}
60#else
61// TODO(kcc): implement this.
62size_t TimeInMilliSeconds() {
63#ifdef __GNUC__
64  return time(0) * 1000;
65#else
66  return WINDOWS::timeGetTime();
67#endif
68}
69#endif
70
71Stats *G_stats;
72
73#ifndef TS_LLVM
74bool GetNameAndOffsetOfGlobalObject(uintptr_t addr,
75                                    string *name, uintptr_t *offset) {
76# ifdef TS_VALGRIND
77    const int kBufLen = 1023;
78    char buff[kBufLen+1];
79    PtrdiffT off;
80    if (VG_(get_datasym_and_offset)(addr, reinterpret_cast<Char*>(buff),
81                                    kBufLen, &off)) {
82      *name = buff;
83      *offset = off;
84      return true;
85    }
86    return false;
87# else
88  return false;
89# endif  // TS_VALGRIND
90}
91#endif  // TS_LLVM
92
93
94#ifndef TS_VALGRIND
95void GetThreadStack(int tid, uintptr_t *min_addr, uintptr_t *max_addr) {
96  *min_addr = 0xfffa;
97  *max_addr = 0xfffb;
98}
99#endif
100
101static int n_errs_found;
102
103void SetNumberOfFoundErrors(int n_errs) {
104  n_errs_found = n_errs;
105}
106
107int GetNumberOfFoundErrors() {
108  return n_errs_found;
109}
110
111
112#if !defined(TS_VALGRIND) && !defined(TS_LLVM)
113FILE *G_out = stderr;
114#endif
115
116#ifdef TS_LLVM
117FILE *G_out;
118#endif
119
120static string RemoveUnsupportedFormat(const char *str) {
121#ifdef _MSC_VER
122  // replace "%'" with "%"
123  string res;
124  size_t n = strlen(str);
125  if (n == 0) {
126    return "";
127  }
128  res.reserve(n);
129  res.push_back(str[0]);
130  for (size_t i = 1; i < n; i++) {
131    if (str[i] == '\'' && *res.rbegin() == '%') continue;
132    res.push_back(str[i]);
133  }
134  return res;
135#else
136  return str;
137#endif
138}
139
140void Printf(const char *format, ...) {
141#ifdef TS_VALGRIND
142  va_list args;
143  va_start(args, format);
144  VG_(vprintf)(format, args);
145  va_end(args);
146#else
147  va_list args;
148  va_start(args, format);
149  vfprintf(G_out, RemoveUnsupportedFormat(format).c_str(), args);
150  fflush(G_out);
151  va_end(args);
152#endif
153}
154
155// Like Print(), but prepend each line with ==XXXXX==,
156// where XXXXX is the pid.
157void Report(const char *format, ...) {
158  int buff_size = 1024*16;
159  char *buff = new char[buff_size];
160  CHECK(buff);
161  DCHECK(G_flags);
162
163  va_list args;
164
165  while (1) {
166    va_start(args, format);
167    int ret = vsnprintf(buff, buff_size,
168                        RemoveUnsupportedFormat(format).c_str(), args);
169    va_end(args);
170    if (ret < buff_size) break;
171    delete [] buff;
172    buff_size *= 2;
173    buff = new char[buff_size];
174    CHECK(buff);
175    // Printf("Resized buff: %d\n", buff_size);
176  }
177
178  char pid_buff[100];
179  snprintf(pid_buff, sizeof(pid_buff), "==%d== ", getpid());
180
181  string res;
182#ifndef TS_LLVM
183  int len = strlen(buff);
184#else
185  int len = __real_strlen(buff);
186#endif
187  bool last_was_new_line = true;
188  for (int i = 0; i < len; i++) {
189    if (G_flags->show_pid && last_was_new_line)
190      res += pid_buff;
191    last_was_new_line = (buff[i] == '\n');
192    res += buff[i];
193  }
194
195  delete [] buff;
196
197  Printf("%s", res.c_str());
198}
199
200long my_strtol(const char *str, char **end, int base) {
201#ifdef TS_VALGRIND
202  if (base == 16 || (base == 0 && str && str[0] == '0' && str[1] == 'x')) {
203    return VG_(strtoll16)((Char*)str, (Char**)end);
204  }
205  return VG_(strtoll10)((Char*)str, (Char**)end);
206#else
207  return strtoll(str, end, base);
208#endif
209}
210
211// Not thread-safe. Need to make it thread-local if we allow
212// malloc to be called concurrently.
213MallocCostCenterStack g_malloc_stack;
214
215size_t GetVmSizeInMb() {
216#ifdef VGO_linux
217  const char *path ="/proc/self/statm";  // see 'man proc'
218  uintptr_t counter = G_stats->read_proc_self_stats++;
219  if (counter >= 1024 && ((counter & (counter - 1)) == 0))
220    Report("INFO: reading %s for %ld'th time\n", path, counter);
221  int  fd = OpenFileReadOnly(path, false);
222  if (fd < 0) return 0;
223  char buff[128];
224  int n_read = read(fd, buff, sizeof(buff) - 1);
225  buff[n_read] = 0;
226  close(fd);
227  char *end;
228  size_t vm_size_in_pages = my_strtol(buff, &end, 10);
229  return vm_size_in_pages >> 8;
230#else
231  return 0;
232#endif
233}
234
235static string StripTemplatesFromFunctionName(const string &fname) {
236  // Returns "" in case of error.
237
238  string ret;
239  size_t read_pointer = 0, braces_depth = 0;
240
241  while (read_pointer < fname.size()) {
242    size_t next_brace = fname.find_first_of("<>", read_pointer);
243    if (next_brace == fname.npos) {
244      if (braces_depth > 0) {
245        // This can happen on Visual Studio if we reach the ~2000 char limit.
246        CHECK(fname.size() > 256);
247        return "";
248      }
249      ret += (fname.c_str() + read_pointer);
250      break;
251    }
252
253    if (braces_depth == 0) {
254      ret.append(fname, read_pointer, next_brace - read_pointer);
255    }
256
257    if (next_brace > 0) {
258      // We could have found one of the following operators.
259      const char *OP[] = {">>=", "<<=",
260                          ">>", "<<",
261                          ">=", "<=",
262                          "->", "->*",
263                          "<", ">"};
264
265      bool operator_name = false;
266      for (size_t i = 0; i < sizeof(OP)/sizeof(*OP); i++) {
267        size_t op_offset = ((string)OP[i]).find(fname[next_brace]);
268        if (op_offset == string::npos)
269          continue;
270        if (next_brace >= 8 + op_offset &&  // 8 == strlen("operator");
271            "operator" == fname.substr(next_brace - (8 + op_offset), 8) &&
272            OP[i] == fname.substr(next_brace - op_offset, strlen(OP[i]))) {
273          operator_name = true;
274          ret += OP[i] + op_offset;
275          next_brace += strlen(OP[i] + op_offset);
276          read_pointer = next_brace;
277          break;
278        }
279      }
280
281      if (operator_name)
282        continue;
283    }
284
285    if (fname[next_brace] == '<') {
286      braces_depth++;
287      read_pointer = next_brace + 1;
288    } else if (fname[next_brace] == '>') {
289      if (braces_depth == 0) {
290        // Going to `braces_depth == -1` IS possible at least for this function on Windows:
291        // "std::operator<<char,std::char_traits<char>,std::allocator<char> >".
292        // Oh, well... Return an empty string and let the caller decide.
293        return "";
294      }
295      braces_depth--;
296      read_pointer = next_brace + 1;
297    } else
298      CHECK(0);
299  }
300  if (braces_depth != 0) {
301    CHECK(fname.size() > 256);
302    return "";
303  }
304  return ret;
305}
306
307static string StripParametersFromFunctionName(const string &demangled) {
308  // Returns "" in case of error.
309
310  string fname = demangled;
311
312  // Strip stuff like "(***)" and "(anonymous namespace)" -> they are tricky.
313  size_t found = fname.npos;
314  while ((found = fname.find(", ")) != fname.npos)
315    fname.erase(found+1, 1);
316  while ((found = fname.find("(**")) != fname.npos)
317    fname.erase(found+2, 1);
318  while ((found = fname.find("(*)")) != fname.npos)
319    fname.erase(found, 3);
320  while ((found = fname.find("const()")) != fname.npos)
321    fname.erase(found+5, 2);
322  while ((found = fname.find("const volatile")) != fname.npos &&
323         found > 1 && found + 14 == fname.size())
324    fname.erase(found-1);
325  while ((found = fname.find("(anonymous namespace)")) != fname.npos)
326    fname.erase(found, 21);
327
328  if (fname.find_first_of("(") == fname.npos)
329    return fname;
330  DCHECK(std::count(fname.begin(), fname.end(), '(') ==
331         std::count(fname.begin(), fname.end(), ')'));
332
333  string ret;
334  bool returns_fun_ptr = false;
335  size_t braces_depth = 0, read_pointer = 0;
336
337  size_t first_parenthesis = fname.find("(");
338  if (first_parenthesis != fname.npos) {
339    DCHECK(fname.find_first_of(")") != fname.npos);
340    DCHECK(fname.find_first_of(")") > first_parenthesis);
341    DCHECK(fname[first_parenthesis] == '(');
342    if (first_parenthesis + 2 < fname.size() &&
343        fname[first_parenthesis - 1] == ' ' &&
344        fname[first_parenthesis + 1] == '*' &&
345        fname[first_parenthesis + 2] != ' ') {
346      // Return value type is a function pointer
347      read_pointer = first_parenthesis + 2;
348      while (fname[read_pointer] == '*' || fname[read_pointer] == '&')
349        read_pointer++;
350      braces_depth = 1;
351      returns_fun_ptr = true;
352    }
353  }
354
355  while (read_pointer < fname.size()) {
356    size_t next_brace = fname.find_first_of("()", read_pointer);
357    if (next_brace == fname.npos) {
358      if (braces_depth != 0) {
359        // Overflow?
360        return "";
361      }
362      size_t _const = fname.find(" const", read_pointer);
363      if (_const == fname.npos) {
364        ret += (fname.c_str() + read_pointer);
365      } else {
366        CHECK(_const + 6 == fname.size());
367        ret.append(fname, read_pointer, _const - read_pointer);
368      }
369      break;
370    }
371
372    if (braces_depth == (returns_fun_ptr ? 1 : 0)) {
373      ret.append(fname, read_pointer, next_brace - read_pointer);
374      returns_fun_ptr = false;
375    }
376
377    if (fname[next_brace] == '(') {
378      if (next_brace >= 8 && fname[next_brace+1] == ')' &&
379          "operator" == fname.substr(next_brace - 8, 8)) {
380        ret += "()";
381        read_pointer = next_brace + 2;
382      } else {
383        braces_depth++;
384        read_pointer = next_brace + 1;
385      }
386    } else if (fname[next_brace] == ')') {
387      CHECK(braces_depth > 0);
388      braces_depth--;
389      read_pointer = next_brace + 1;
390    } else
391      CHECK(0);
392  }
393  if (braces_depth != 0)
394    return "";
395
396  // Special case: on Linux, Valgrind prepends the return type for template
397  // functions. And on Windows we may see `scalar deleting destructor'.
398  // And we may see "operaror new" etc.
399  // And some STL code inserts const& between the return type and the function
400  // name.
401  // Oh, well...
402  size_t space_or_tick;
403  while (ret != "") {
404    space_or_tick = ret.find_first_of("` ");
405    if (space_or_tick != ret.npos && ret[space_or_tick] == ' ' &&
406        ret.substr(0, space_or_tick).find("operator") == string::npos) {
407      ret = ret.substr(space_or_tick + 1);
408    } else if (space_or_tick != ret.npos && space_or_tick + 1 == ret.size()) {
409      ret = ret.substr(0, space_or_tick);
410    } else {
411      break;
412    }
413  }
414  return ret;
415}
416
417string NormalizeFunctionName(const string &demangled) {
418  if (demangled[1] == '[' && strchr("+-=", demangled[0]) != NULL) {
419    // Objective-C function
420    return demangled;
421  }
422
423  if (demangled.find_first_of("<>()") == demangled.npos) {
424    // C function or a well-formatted function name.
425    return demangled;
426  }
427
428  if (demangled == "(below main)" || demangled == "(no symbols)")
429    return demangled;
430
431  const char* const MALFORMED = "(malformed frame)";
432
433  string fname = StripTemplatesFromFunctionName(demangled);
434  if (fname.size() == 0) {
435    if (DEBUG_MODE)
436      Printf("PANIC: `%s`\n", demangled.c_str());
437    return MALFORMED;
438  }
439
440  fname = StripParametersFromFunctionName(fname);
441  if (fname.size() == 0) {
442    CHECK(demangled.size() >= 256);
443    if (DEBUG_MODE)
444      Printf("PANIC: `%s`\n", demangled.c_str());
445    return MALFORMED;
446  }
447
448  return fname;
449}
450
451void OpenFileWriteStringAndClose(const string &file_name, const string &str) {
452#ifdef TS_VALGRIND
453  SysRes sres = VG_(open)((const Char*)file_name.c_str(),
454                          VKI_O_WRONLY|VKI_O_CREAT|VKI_O_TRUNC,
455                          VKI_S_IRUSR|VKI_S_IWUSR);
456  if (sr_isError(sres)) {
457    Report("WARNING: can not open file %s\n", file_name.c_str());
458    exit(1);
459  }
460  int fd = sr_Res(sres);
461  write(fd, str.c_str(), str.size());
462  close(fd);
463#else
464  CHECK(0);
465#endif
466}
467
468//--------- Sockets ------------------ {{{1
469#if defined(TS_PIN) && defined(__GNUC__)
470#include <sys/types.h>
471#include <sys/socket.h>
472#include <netinet/in.h>
473#include <netdb.h>
474FILE *OpenSocketForWriting(const string &host_and_port) {
475  size_t col = host_and_port.find(":");
476  if (col == string::npos) return NULL;
477  string host = host_and_port.substr(0, col);
478  string port_str = host_and_port.substr(col + 1);
479  int sockfd;
480  struct sockaddr_in serv_addr;
481  struct hostent *server;
482  sockfd = socket(AF_INET, SOCK_STREAM, 0);
483  if (sockfd < 0) return NULL;
484  server = gethostbyname(host.c_str());
485  if (server == 0) return NULL;
486  memset(&serv_addr, 0, sizeof(serv_addr));
487  serv_addr.sin_family = AF_INET;
488  memcpy((char *)&serv_addr.sin_addr.s_addr,
489         (char *)server->h_addr,
490         server->h_length);
491  serv_addr.sin_port = htons(atoi(port_str.c_str()));
492  if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
493    return NULL;
494  return fdopen(sockfd, "w");
495}
496#else
497FILE *OpenSocketForWriting(const string &host_and_port) {
498  return NULL;  // unimplemented.
499}
500#endif
501//--------- TSLock ------------------ {{{1
502#ifdef _MSC_VER
503//# define TS_LOCK_PIPE
504# define TS_LOCK_PIN
505#else
506# define TS_LOCK_FUTEX
507#endif
508
509#if defined(TS_LOCK_PIPE) && defined(TS_PIN)
510#ifdef __GNUC__
511#include <unistd.h>
512// Lock based on pipe's send/receive. The idea (but not the code)
513// is shamelessly stolen from valgrind's /coregrind/m_scheduler/sema.c
514struct TSLock::Rep {
515  bool held;
516  char pipe_char;
517  int pipe_fd[2];
518
519  void Write() {
520    char buf[2];
521    buf[0] = pipe_char;
522    buf[1] = 0;
523    int res = write(pipe_fd[1], buf, 1);
524    CHECK(res == 1);
525  }
526  bool Read() {
527    char buf[2];
528    buf[0] = 0;
529    buf[1] = 0;
530    int res = read(pipe_fd[0], buf, 1);
531    if (res != 1)
532      return false;
533    //Printf("rep::Read: %c\n", buf[0]);
534
535    pipe_char++;
536    if (pipe_char == 'Z' + 1) pipe_char = 'A';
537    return true;
538  }
539  void Open() {
540    CHECK(0 == pipe(pipe_fd));
541    CHECK(pipe_fd[0] != pipe_fd[1]);
542    pipe_char = 'A';
543  }
544  void Close() {
545    close(pipe_fd[0]);
546    close(pipe_fd[1]);
547  }
548};
549#elif defined(_MSC_VER)
550struct TSLock::Rep {
551  bool held;
552  char pipe_char;
553  WINDOWS::HANDLE pipe_fd[2];
554  void Write() {
555    char buf[2];
556    buf[0] = pipe_char;
557    buf[1] = 0;
558    WINDOWS::DWORD n_written = 0;
559    int res = WINDOWS::WriteFile(pipe_fd[1], buf, 1, &n_written, NULL);
560    CHECK(res != 0 && n_written == 1);
561  }
562  bool Read() {
563    char buf[2];
564    buf[0] = 0;
565    buf[1] = 0;
566    WINDOWS::DWORD n_read  = 0;
567    int res = WINDOWS::ReadFile(pipe_fd[0], buf, 1, &n_read, NULL);
568    if (res == 0 && n_read == 0)
569      return false;
570    //Printf("rep::Read: %c\n", buf[0]);
571
572    pipe_char++;
573    if (pipe_char == 'Z' + 1) pipe_char = 'A';
574    return true;
575  }
576  void Open() {
577    CHECK(WINDOWS::CreatePipe(&pipe_fd[0], &pipe_fd[1], NULL, 0));
578    CHECK(pipe_fd[0] != pipe_fd[1]);
579    pipe_char = 'A';
580  }
581  void Close() {
582    WINDOWS::CloseHandle(pipe_fd[0]);
583    WINDOWS::CloseHandle(pipe_fd[1]);
584  }
585};
586#endif
587
588TSLock::TSLock() {
589  rep_ = new Rep;
590  rep_->held = false;
591  rep_->Open();
592  rep_->Write();
593}
594TSLock::~TSLock() {
595  rep_->Close();
596}
597void TSLock::Lock() {
598  while(rep_->Read() == false)
599    ;
600  rep_->held = true;
601}
602void TSLock::Unlock() {
603  rep_->held = false;
604  rep_->Write();
605}
606void TSLock::AssertHeld() {
607  DCHECK(rep_->held);
608}
609#endif  // __GNUC__ & TS_LOCK_PIPE
610
611#if defined(TS_LOCK_PIN) && defined(TS_PIN)
612#include "pin.H"
613struct TSLock::Rep {
614  PIN_LOCK lock;
615  bool held;
616};
617
618TSLock::TSLock() {
619  rep_ = new Rep();
620  rep_->held = false;
621  InitLock(&rep_->lock);
622}
623TSLock::~TSLock() {
624  delete rep_;
625}
626void TSLock::Lock() {
627  GetLock(&rep_->lock, __LINE__);
628  rep_->held = true;
629}
630void TSLock::Unlock() {
631  rep_->held = false;
632  ReleaseLock(&rep_->lock);
633}
634void TSLock::AssertHeld() {
635  DCHECK(rep_->held);
636}
637#endif  // TS_LOCK_PIN
638
639#if defined(TS_WRAP_PTHREAD_LOCK)
640#include "tsan_rtl_wrap.h"
641
642struct TSLock::Rep {
643  pthread_mutex_t lock;
644  bool held;
645};
646TSLock::TSLock() {
647  rep_ = new Rep();
648  rep_->held = false;
649  __real_pthread_mutex_init(&rep_->lock, NULL);
650}
651TSLock::~TSLock() {
652  __real_pthread_mutex_destroy(&rep_->lock);
653  delete rep_;
654}
655void TSLock::Lock() {
656  __real_pthread_mutex_lock(&rep_->lock);
657  rep_->held = true;
658}
659void TSLock::Unlock() {
660  rep_->held = false;
661  __real_pthread_mutex_unlock(&rep_->lock);
662}
663void TSLock::AssertHeld() {
664  DCHECK(rep_->held);
665}
666#endif  // TS_LLVM
667
668#if defined(TS_LOCK_FUTEX) && defined(__GNUC__) && \
669 (defined (TS_PIN) || defined (TS_LLVM))
670#include <linux/futex.h>
671#include <sys/time.h>
672#include <syscall.h>
673
674// Simple futex-based lock.
675// The idea is taken from "Futexes Are Tricky" by Ulrich Drepper
676
677TSLock::TSLock() {
678  rep_ = 0;
679  ANNOTATE_BENIGN_RACE(&rep_, "Benign race on TSLock::rep_");
680  ANNOTATE_RWLOCK_CREATE(this);
681}
682TSLock::~TSLock() {
683  ANNOTATE_RWLOCK_DESTROY(this);
684  DCHECK(rep_ == 0);
685}
686void TSLock::Lock() {
687  int *p = (int*)&rep_;
688  const int kSpinCount = 100;
689  DCHECK(kSpinCount > 0);
690  int c;
691  for (int i = 0; i < kSpinCount; i++) {
692    c = __sync_val_compare_and_swap(p, 0, 1);
693    if (c == 0) break;
694  }
695  if (c == 0) {
696    // The mutex was unlocked. Now it's ours. Done.
697    ANNOTATE_RWLOCK_ACQUIRED(this, /*is_w*/true);
698    return;
699  }
700  DCHECK(c == 1 || c == 2);
701  // We are going to block on this lock. Make sure others know that.
702  if (c != 2) {
703    c = __sync_lock_test_and_set(p, 2);
704  }
705  // Block.
706  int n_waits = 0;
707  while (c != 0) {
708    syscall(SYS_futex, p, FUTEX_WAIT, 2, 0, 0, 0);
709    n_waits++;
710    c = __sync_lock_test_and_set(p, 2);
711  }
712  ANNOTATE_RWLOCK_ACQUIRED(this, /*is_w*/true);
713  G_stats->futex_wait += n_waits;
714}
715void TSLock::Unlock() {
716  ANNOTATE_RWLOCK_RELEASED(this, /*is_w*/true);
717  int *p = (int*)&rep_;
718  DCHECK(*p == 1 || *p == 2);
719  int c = __sync_sub_and_fetch(p, 1);
720  DCHECK(c == 0 || c == 1);
721  if (c == 1) {
722    *p = 0;
723    syscall(SYS_futex, p, FUTEX_WAKE, 1, 0, 0, 0);
724  }
725}
726void TSLock::AssertHeld() {
727  DCHECK(rep_);
728}
729#endif
730
731
732//--------------- Atomics ----------------- {{{1
733#if defined (_MSC_VER) && TS_SERIALIZED == 0
734uintptr_t AtomicExchange(uintptr_t *ptr, uintptr_t new_value) {
735  return _InterlockedExchange((volatile WINDOWS::LONG*)ptr, new_value);
736}
737
738void ReleaseStore(uintptr_t *ptr, uintptr_t value) {
739  *(volatile uintptr_t*)ptr = value;
740  // TODO(kcc): anything to add here?
741}
742
743int32_t NoBarrier_AtomicIncrement(int32_t* ptr) {
744  return _InterlockedIncrement((volatile WINDOWS::LONG *)ptr);
745}
746
747int32_t NoBarrier_AtomicDecrement(int32_t* ptr) {
748  return _InterlockedDecrement((volatile WINDOWS::LONG *)ptr);
749}
750#endif  // _MSC_VER && TS_SERIALIZED
751//--------------- YIELD ----------------- {{{1
752#if defined (_MSC_VER)
753void YIELD() {
754  WINDOWS::Sleep(0);
755}
756#elif defined(TS_VALGRIND)
757void YIELD() {
758}
759#elif defined(__GNUC__)
760void YIELD() {
761  sched_yield();
762}
763#else
764#error "Unknown config"
765#endif
766
767// end. {{{1
768// vim:shiftwidth=2:softtabstop=2:expandtab:tw=80
769