/*
 *
 * Copyright 2017 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Package primitives_test contains benchmarks for various synchronization primitives
// available in Go.
package primitives_test

import (
	"sync"
	"sync/atomic"
	"testing"
)

func BenchmarkSelectClosed(b *testing.B) {
	c := make(chan struct{})
	close(c)
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		select {
		case <-c:
			x++
		default:
		}
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

func BenchmarkSelectOpen(b *testing.B) {
	c := make(chan struct{})
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		select {
		case <-c:
		default:
			x++
		}
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

func BenchmarkAtomicBool(b *testing.B) {
	c := int32(0)
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if atomic.LoadInt32(&c) == 0 {
			x++
		}
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

func BenchmarkAtomicValue(b *testing.B) {
	c := atomic.Value{}
	c.Store(0)
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if c.Load().(int) == 0 {
			x++
		}
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

func BenchmarkMutex(b *testing.B) {
	c := sync.Mutex{}
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		c.Lock()
		x++
		c.Unlock()
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

func BenchmarkRWMutex(b *testing.B) {
	c := sync.RWMutex{}
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		c.RLock()
		x++
		c.RUnlock()
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

func BenchmarkRWMutexW(b *testing.B) {
	c := sync.RWMutex{}
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		c.Lock()
		x++
		c.Unlock()
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

func BenchmarkMutexWithDefer(b *testing.B) {
	c := sync.Mutex{}
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		func() {
			c.Lock()
			defer c.Unlock()
			x++
		}()
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

func BenchmarkMutexWithClosureDefer(b *testing.B) {
	c := sync.Mutex{}
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		func() {
			c.Lock()
			defer func() { c.Unlock() }()
			x++
		}()
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

func BenchmarkMutexWithoutDefer(b *testing.B) {
	c := sync.Mutex{}
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		func() {
			c.Lock()
			x++
			c.Unlock()
		}()
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

type myFooer struct{}

func (myFooer) Foo() {}

type fooer interface {
	Foo()
}

func BenchmarkInterfaceTypeAssertion(b *testing.B) {
	// Call a separate function to avoid compiler optimizations.
	runInterfaceTypeAssertion(b, myFooer{})
}

func runInterfaceTypeAssertion(b *testing.B, fer interface{}) {
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if _, ok := fer.(fooer); ok {
			x++
		}
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}

func BenchmarkStructTypeAssertion(b *testing.B) {
	// Call a separate function to avoid compiler optimizations.
	runStructTypeAssertion(b, myFooer{})
}

func runStructTypeAssertion(b *testing.B, fer interface{}) {
	x := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if _, ok := fer.(myFooer); ok {
			x++
		}
	}
	b.StopTimer()
	if x != b.N {
		b.Fatal("error")
	}
}
