/* Copyright (c) 2008-2010, 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.
 *     * 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.
 */

// This file is part of ThreadSanitizer, a dynamic data race detector.
// Author: Konstantin Serebryany.

#ifndef TS_LOCK_H_
#define TS_LOCK_H_

#include "ts_util.h"

#if (DEBUG > 0) && (TS_SERIALIZED == 0) && defined (TS_LLVM) && !defined(DYNAMIC_ANNOTATIONS_ENABLED)
# define DYNAMIC_ANNOTATIONS_ENABLED 1
#endif
#include "dynamic_annotations.h"

//--------- Simple Lock ------------------ {{{1
#if defined(TS_VALGRIND) || defined(TS_OFFLINE)
class TSLock {
 public:
  void Lock() {};
  void Unlock() {};
  void AssertHeld() {};
};
#else
class TSLock {
 public:
  TSLock();
  ~TSLock();
  void Lock();
  void Unlock();
  void AssertHeld();
 private:
  struct Rep;
  Rep *rep_;
};
#endif

class ScopedLock {
 public:
  ScopedLock(TSLock *lock)
    : lock_(lock) {
    lock_->Lock();
  }
  ~ScopedLock() { lock_->Unlock(); }
 private:
  TSLock *lock_;
};

//--------- Atomic operations {{{1
#if TS_SERIALIZED == 1
// No need for atomics when all ThreadSanitizer logic is serialized.
ALWAYS_INLINE uintptr_t AtomicExchange(uintptr_t *ptr, uintptr_t new_value) {
  uintptr_t old_value = *ptr;
  *ptr = new_value;
  return old_value;
}

ALWAYS_INLINE void ReleaseStore(uintptr_t *ptr, uintptr_t value) {
  *ptr = value;
}

ALWAYS_INLINE int32_t NoBarrier_AtomicIncrement(int32_t* ptr) {
  return *ptr += 1;
}

ALWAYS_INLINE int32_t NoBarrier_AtomicDecrement(int32_t* ptr) {
  return *ptr -= 1;
}

#elif defined(__GNUC__)

ALWAYS_INLINE uintptr_t AtomicExchange(uintptr_t *ptr, uintptr_t new_value) {
  return __sync_lock_test_and_set(ptr, new_value);
}

ALWAYS_INLINE void ReleaseStore(uintptr_t *ptr, uintptr_t value) {
  __asm__ __volatile__("" : : : "memory");
  *(volatile uintptr_t*)ptr = value;
}

ALWAYS_INLINE int32_t NoBarrier_AtomicIncrement(int32_t* ptr) {
  return __sync_add_and_fetch(ptr, 1);
}

ALWAYS_INLINE int32_t NoBarrier_AtomicDecrement(int32_t* ptr) {
  return __sync_sub_and_fetch(ptr, 1);
}

#elif defined(_MSC_VER)
uintptr_t AtomicExchange(uintptr_t *ptr, uintptr_t new_value);
void ReleaseStore(uintptr_t *ptr, uintptr_t value);
int32_t NoBarrier_AtomicIncrement(int32_t* ptr);
int32_t NoBarrier_AtomicDecrement(int32_t* ptr);

#else
# error "unsupported configuration"
#endif


ALWAYS_INLINE int32_t AtomicIncrementRefcount(int32_t *refcount) {
  return NoBarrier_AtomicIncrement(refcount);
}

ALWAYS_INLINE int32_t AtomicDecrementRefcount(int32_t *refcount) {
  ANNOTATE_HAPPENS_BEFORE(refcount);
  int32_t res = NoBarrier_AtomicDecrement(refcount);
  if (res == 0) {
    ANNOTATE_HAPPENS_AFTER(refcount);
  }
  return res;
}



// end. {{{1
#endif  // TS_LOCK_H_
// vim:shiftwidth=2:softtabstop=2:expandtab:tw=80