// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build race

package atomic

import (
	"runtime"
	"unsafe"
)

// We use runtime.RaceRead() inside of atomic operations to catch races
// between atomic and non-atomic operations.  It will also catch races
// between Mutex.Lock() and mutex overwrite (mu = Mutex{}).  Since we use
// only RaceRead() we won't catch races with non-atomic loads.
// Otherwise (if we use RaceWrite()) we will report races
// between atomic operations (false positives).

var mtx uint32 = 1 // same for all

func SwapInt32(addr *int32, new int32) (old int32) {
	return int32(SwapUint32((*uint32)(unsafe.Pointer(addr)), uint32(new)))
}

func SwapUint32(addr *uint32, new uint32) (old uint32) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	old = *addr
	*addr = new
	runtime.RaceReleaseMerge(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
	return
}

func SwapInt64(addr *int64, new int64) (old int64) {
	return int64(SwapUint64((*uint64)(unsafe.Pointer(addr)), uint64(new)))
}

func SwapUint64(addr *uint64, new uint64) (old uint64) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	old = *addr
	*addr = new
	runtime.RaceReleaseMerge(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
	return
}

func SwapUintptr(addr *uintptr, new uintptr) (old uintptr) {
	return uintptr(SwapPointer((*unsafe.Pointer)(unsafe.Pointer(addr)), unsafe.Pointer(new)))
}

func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	old = *addr
	*addr = new
	runtime.RaceReleaseMerge(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
	return
}

func CompareAndSwapInt32(val *int32, old, new int32) bool {
	return CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new))
}

func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool) {
	_ = *val
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func CompareAndSwapInt64(val *int64, old, new int64) bool {
	return CompareAndSwapUint64((*uint64)(unsafe.Pointer(val)), uint64(old), uint64(new))
}

func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool) {
	_ = *val
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) {
	_ = *val
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool) {
	_ = *val
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func AddInt32(val *int32, delta int32) int32 {
	return int32(AddUint32((*uint32)(unsafe.Pointer(val)), uint32(delta)))
}

func AddUint32(val *uint32, delta uint32) (new uint32) {
	_ = *val
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	*val = *val + delta
	new = *val
	runtime.RaceReleaseMerge(unsafe.Pointer(val))
	runtime.RaceSemrelease(&mtx)

	return
}

func AddInt64(val *int64, delta int64) int64 {
	return int64(AddUint64((*uint64)(unsafe.Pointer(val)), uint64(delta)))
}

func AddUint64(val *uint64, delta uint64) (new uint64) {
	_ = *val
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	*val = *val + delta
	new = *val
	runtime.RaceReleaseMerge(unsafe.Pointer(val))
	runtime.RaceSemrelease(&mtx)

	return
}

func AddUintptr(val *uintptr, delta uintptr) (new uintptr) {
	_ = *val
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	*val = *val + delta
	new = *val
	runtime.RaceReleaseMerge(unsafe.Pointer(val))
	runtime.RaceSemrelease(&mtx)

	return
}

func LoadInt32(addr *int32) int32 {
	return int32(LoadUint32((*uint32)(unsafe.Pointer(addr))))
}

func LoadUint32(addr *uint32) (val uint32) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func LoadInt64(addr *int64) int64 {
	return int64(LoadUint64((*uint64)(unsafe.Pointer(addr))))
}

func LoadUint64(addr *uint64) (val uint64) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func LoadUintptr(addr *uintptr) (val uintptr) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func StoreInt32(addr *int32, val int32) {
	StoreUint32((*uint32)(unsafe.Pointer(addr)), uint32(val))
}

func StoreUint32(addr *uint32, val uint32) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}

func StoreInt64(addr *int64, val int64) {
	StoreUint64((*uint64)(unsafe.Pointer(addr)), uint64(val))
}

func StoreUint64(addr *uint64, val uint64) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}

func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}

func StoreUintptr(addr *uintptr, val uintptr) {
	_ = *addr
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}
