Skip to content

Commit

Permalink
Fix potential race between NullValue and IsNullValue (#22410)
Browse files Browse the repository at this point in the history
Note that since IsNullValue is on a hot path, it must never block in the
common case.
  • Loading branch information
jhendrixMSFT authored Feb 20, 2024
1 parent ec67edd commit 9732d67
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 1 deletion.
2 changes: 2 additions & 0 deletions sdk/azcore/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

### Bugs Fixed

* Fixed a potential race condition between `NullValue` and `IsNullValue`.

### Other Changes

## 1.9.2 (2024-02-06)
Expand Down
21 changes: 20 additions & 1 deletion sdk/azcore/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package azcore

import (
"reflect"
"sync"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
Expand Down Expand Up @@ -41,13 +42,28 @@ func NewSASCredential(sas string) *SASCredential {
}

// holds sentinel values used to send nulls
var nullables map[reflect.Type]interface{} = map[reflect.Type]interface{}{}
var nullables map[reflect.Type]any = map[reflect.Type]any{}
var nullablesMu sync.RWMutex

// NullValue is used to send an explicit 'null' within a request.
// This is typically used in JSON-MERGE-PATCH operations to delete a value.
func NullValue[T any]() T {
t := shared.TypeOfT[T]()

nullablesMu.RLock()
v, found := nullables[t]
nullablesMu.RUnlock()

if found {
// return the sentinel object
return v.(T)
}

// promote to exclusive lock and check again (double-checked locking pattern)
nullablesMu.Lock()
defer nullablesMu.Unlock()
v, found = nullables[t]

if !found {
var o reflect.Value
if k := t.Kind(); k == reflect.Map {
Expand All @@ -72,6 +88,9 @@ func NullValue[T any]() T {
func IsNullValue[T any](v T) bool {
// see if our map has a sentinel object for this *T
t := reflect.TypeOf(v)
nullablesMu.RLock()
defer nullablesMu.RUnlock()

if o, found := nullables[t]; found {
o1 := reflect.ValueOf(o)
v1 := reflect.ValueOf(v)
Expand Down

0 comments on commit 9732d67

Please sign in to comment.