/* * * 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. * */ // Benchmark options for safe config selector type. package primitives_test import ( "sync" "sync/atomic" "testing" "time" "unsafe" ) type safeUpdaterAtomicAndCounter struct { ptr unsafe.Pointer // *countingFunc } type countingFunc struct { mu sync.RWMutex f func() } func (s *safeUpdaterAtomicAndCounter) call() { cfPtr := atomic.LoadPointer(&s.ptr) var cf *countingFunc for { cf = (*countingFunc)(cfPtr) cf.mu.RLock() cfPtr2 := atomic.LoadPointer(&s.ptr) if cfPtr == cfPtr2 { // Use cf with confidence! break } // cf changed; try to use the new one instead, because the old one is // no longer valid to use. cf.mu.RUnlock() cfPtr = cfPtr2 } defer cf.mu.RUnlock() cf.f() } func (s *safeUpdaterAtomicAndCounter) update(f func()) { newCF := &countingFunc{f: f} oldCFPtr := atomic.SwapPointer(&s.ptr, unsafe.Pointer(newCF)) if oldCFPtr == nil { return } (*countingFunc)(oldCFPtr).mu.Lock() (*countingFunc)(oldCFPtr).mu.Unlock() //lint:ignore SA2001 necessary to unlock after locking to unblock any RLocks } type safeUpdaterRWMutex struct { mu sync.RWMutex f func() } func (s *safeUpdaterRWMutex) call() { s.mu.RLock() defer s.mu.RUnlock() s.f() } func (s *safeUpdaterRWMutex) update(f func()) { s.mu.Lock() defer s.mu.Unlock() s.f = f } type updater interface { call() update(f func()) } func benchmarkSafeUpdater(b *testing.B, u updater) { t := time.NewTicker(time.Second) go func() { for range t.C { u.update(func() {}) } }() b.RunParallel(func(pb *testing.PB) { u.update(func() {}) for pb.Next() { u.call() } }) t.Stop() } func BenchmarkSafeUpdaterAtomicAndCounter(b *testing.B) { benchmarkSafeUpdater(b, &safeUpdaterAtomicAndCounter{}) } func BenchmarkSafeUpdaterRWMutex(b *testing.B) { benchmarkSafeUpdater(b, &safeUpdaterRWMutex{}) }