blob: 12fd3f271fe9e7a134ef1f94fb95ef320a12db3d [file] [log] [blame]
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +03001/* 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
Evgeniy Stepanovdb2b85a2011-04-19 16:47:40 +040073#ifndef TS_LLVM
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +030074bool GetNameAndOffsetOfGlobalObject(uintptr_t addr,
75 string *name, uintptr_t *offset) {
Evgeniy Stepanovdb2b85a2011-04-19 16:47:40 +040076# ifdef TS_VALGRIND
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +030077 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;
Evgeniy Stepanovdb2b85a2011-04-19 16:47:40 +040087# else
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +030088 return false;
Evgeniy Stepanovdb2b85a2011-04-19 16:47:40 +040089# endif // TS_VALGRIND
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +030090}
Evgeniy Stepanovdb2b85a2011-04-19 16:47:40 +040091#endif // TS_LLVM
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +030092
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
Evgeniy Stepanovdb2b85a2011-04-19 16:47:40 +0400235static 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;
Evgeniy Stepanovb32f5802011-12-20 11:21:56 +0400266 for (size_t i = 0; i < TS_ARRAY_SIZE(OP); i++) {
Evgeniy Stepanovdb2b85a2011-04-19 16:47:40 +0400267 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;
Evgeniy Stepanovb32f5802011-12-20 11:21:56 +0400330 DCHECK(count(fname.begin(), fname.end(), '(') ==
331 count(fname.begin(), fname.end(), ')'));
Evgeniy Stepanovdb2b85a2011-04-19 16:47:40 +0400332
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
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +0300451void 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
Evgeniy Stepanovb32f5802011-12-20 11:21:56 +0400731// Same as above to compile Go's rtl
732// No annotations in this version: it should be simple as possible.
733#if defined(TS_LOCK_FUTEX) && defined(__GNUC__) && \
734 (defined (TS_GO))
735#include <linux/futex.h> // TODO(mpimenov): portability?
736#include <sys/time.h>
737#include <syscall.h>
738
739// Simple futex-based lock.
740// The idea is taken from "Futexes Are Tricky" by Ulrich Drepper
741
742TSLock::TSLock() {
743 rep_ = 0;
744}
745TSLock::~TSLock() {
746 DCHECK(rep_ == 0);
747}
748void TSLock::Lock() {
749 int *p = (int*)&rep_;
750 const int kSpinCount = 100;
751 DCHECK(kSpinCount > 0);
752 int c;
753 for (int i = 0; i < kSpinCount; i++) {
754 c = __sync_val_compare_and_swap(p, 0, 1);
755 if (c == 0) break;
756 }
757 if (c == 0) {
758 // The mutex was unlocked. Now it's ours. Done.
759 return;
760 }
761 DCHECK(c == 1 || c == 2);
762 // We are going to block on this lock. Make sure others know that.
763 if (c != 2) {
764 c = __sync_lock_test_and_set(p, 2);
765 }
766 // Block.
767 int n_waits = 0;
768 while (c != 0) {
769 syscall(SYS_futex, p, FUTEX_WAIT, 2, 0, 0, 0);
770 n_waits++;
771 c = __sync_lock_test_and_set(p, 2);
772 }
773 G_stats->futex_wait += n_waits;
774}
775void TSLock::Unlock() {
776 int *p = (int*)&rep_;
777 DCHECK(*p == 1 || *p == 2);
778 int c = __sync_sub_and_fetch(p, 1);
779 DCHECK(c == 0 || c == 1);
780 if (c == 1) {
781 *p = 0;
782 syscall(SYS_futex, p, FUTEX_WAKE, 1, 0, 0, 0);
783 }
784}
785void TSLock::AssertHeld() {
786 DCHECK(rep_);
787}
788#endif // (TS_LOCK_FUTEX) (__GNUC__) && (TS_GO)
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +0300789
790//--------------- Atomics ----------------- {{{1
791#if defined (_MSC_VER) && TS_SERIALIZED == 0
792uintptr_t AtomicExchange(uintptr_t *ptr, uintptr_t new_value) {
793 return _InterlockedExchange((volatile WINDOWS::LONG*)ptr, new_value);
794}
795
796void ReleaseStore(uintptr_t *ptr, uintptr_t value) {
797 *(volatile uintptr_t*)ptr = value;
798 // TODO(kcc): anything to add here?
799}
800
801int32_t NoBarrier_AtomicIncrement(int32_t* ptr) {
802 return _InterlockedIncrement((volatile WINDOWS::LONG *)ptr);
803}
804
805int32_t NoBarrier_AtomicDecrement(int32_t* ptr) {
806 return _InterlockedDecrement((volatile WINDOWS::LONG *)ptr);
807}
808#endif // _MSC_VER && TS_SERIALIZED
809//--------------- YIELD ----------------- {{{1
810#if defined (_MSC_VER)
Evgeniy Stepanovb32f5802011-12-20 11:21:56 +0400811#include <intrin.h>
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +0300812void YIELD() {
813 WINDOWS::Sleep(0);
814}
Evgeniy Stepanovb32f5802011-12-20 11:21:56 +0400815void PROCESSOR_YIELD() {
816 _mm_pause();
817}
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +0300818#elif defined(TS_VALGRIND)
819void YIELD() {
820}
Evgeniy Stepanovb32f5802011-12-20 11:21:56 +0400821void PROCESSOR_YIELD() {
822}
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +0300823#elif defined(__GNUC__)
824void YIELD() {
825 sched_yield();
826}
Evgeniy Stepanovb32f5802011-12-20 11:21:56 +0400827void PROCESSOR_YIELD() {
828 __asm__ __volatile__ ("pause");
829}
Evgeniy Stepanovbec2f0e2011-03-05 12:50:33 +0300830#else
831#error "Unknown config"
832#endif
833
834// end. {{{1
835// vim:shiftwidth=2:softtabstop=2:expandtab:tw=80