Skip to content

Commit a54cc5e

Browse files
Effi-SAkramBitar
authored andcommitted
Added mutexes
Signed-off-by: Effi-S <effi.szt@gmail.com>
1 parent d5fa476 commit a54cc5e

4 files changed

Lines changed: 110 additions & 10 deletions

File tree

token/services/identity/deserializer/deserializer_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package deserializer
99
import (
1010
"context"
1111
"errors"
12+
"sync"
1213
"testing"
1314

1415
"github.com/LFDT-Panurus/panurus/token/driver"
@@ -880,3 +881,76 @@ func TestTypedIdentityVerifierDeserializer(t *testing.T) {
880881
assert.Equal(t, 1, mockMatcherDeserializer.GetAuditInfoMatcherCallCount())
881882
})
882883
}
884+
885+
// TestDeserializerMultiplexConcurrency exercises the three deserializer
886+
// multiplexes under concurrent registration (map writes) and deserialization
887+
// (map reads). Without the internal mutex this reliably trips the race
888+
// detector or the runtime's fatal "concurrent map read and map write" check;
889+
// with it, the test runs clean. Run under `go test -race` for full coverage.
890+
func TestDeserializerMultiplexConcurrency(t *testing.T) {
891+
const (
892+
goroutines = 16
893+
iterations = 200
894+
)
895+
896+
signerMultiplex := NewTypedSignerDeserializerMultiplex()
897+
verifierMultiplex := NewTypedVerifierDeserializerMultiplex()
898+
eidrhDeserializer := NewEIDRHDeserializer()
899+
900+
// Pre-register a deserializer for the type the readers look up so the read
901+
// paths traverse the stored slice rather than bailing out on a miss.
902+
mockSigner := &drivermock.Signer{}
903+
signerDes := &identitydrivermock.TypedSignerDeserializer{}
904+
signerDes.DeserializeSignerReturns(mockSigner, nil)
905+
signerMultiplex.AddTypedSignerDeserializer(identity.Type(99), signerDes)
906+
907+
mockVerifier := &drivermock.Verifier{}
908+
verifierDes := &identitydrivermock.TypedVerifierDeserializer{}
909+
verifierDes.DeserializeVerifierReturns(mockVerifier, nil)
910+
verifierDes.RecipientsReturns([]driver.Identity{[]byte("recipient")}, nil)
911+
verifierDes.GetAuditInfoMatcherReturns(&drivermock.Matcher{}, nil)
912+
verifierDes.GetAuditInfoReturns([]byte("audit-info"), nil)
913+
verifierMultiplex.AddTypedVerifierDeserializer(identity.Type(99), verifierDes)
914+
915+
mockAuditInfo := &identitydrivermock.AuditInfo{}
916+
auditDes := &identitydrivermock.AuditInfoDeserializer{}
917+
auditDes.DeserializeAuditInfoReturns(mockAuditInfo, nil)
918+
eidrhDeserializer.AddDeserializer(identity.Type(99), auditDes)
919+
920+
typedID := createTypedIdentity(t, identity.Type(99), []byte("raw-identity"))
921+
provider := &drivermock.AuditInfoProvider{}
922+
923+
var wg sync.WaitGroup
924+
925+
// Writers: keep registering new deserializers concurrently.
926+
for range goroutines {
927+
wg.Go(func() {
928+
for range iterations {
929+
// Register under a distinct type so writers keep hammering the
930+
// same map concurrently without clobbering the entry the
931+
// readers rely on below.
932+
signerMultiplex.AddTypedSignerDeserializer(identity.Type(100), &identitydrivermock.TypedSignerDeserializer{})
933+
verifierMultiplex.AddTypedVerifierDeserializer(identity.Type(100), &identitydrivermock.TypedVerifierDeserializer{})
934+
eidrhDeserializer.AddDeserializer(identity.Type(100), &identitydrivermock.AuditInfoDeserializer{})
935+
}
936+
})
937+
}
938+
939+
// Readers: keep deserializing concurrently.
940+
for range goroutines {
941+
wg.Go(func() {
942+
ctx := context.Background()
943+
for range iterations {
944+
_, _ = signerMultiplex.DeserializeSigner(ctx, typedID)
945+
_, _ = verifierMultiplex.DeserializeVerifier(ctx, typedID)
946+
_, _ = verifierMultiplex.Recipients(typedID)
947+
_, _ = verifierMultiplex.GetAuditInfoMatcher(ctx, typedID, []byte("audit-info"))
948+
_ = verifierMultiplex.MatchIdentity(ctx, typedID, []byte("audit-info"))
949+
_, _ = verifierMultiplex.GetAuditInfo(ctx, typedID, provider)
950+
_, _, _ = eidrhDeserializer.GetEIDAndRH(ctx, typedID, []byte("audit-info"))
951+
}
952+
})
953+
}
954+
955+
wg.Wait()
956+
}

token/services/identity/deserializer/eidrh.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package deserializer
88

99
import (
1010
"context"
11+
"sync"
1112

1213
"github.com/LFDT-Panurus/panurus/token/driver"
1314
"github.com/LFDT-Panurus/panurus/token/services/identity"
@@ -17,6 +18,7 @@ import (
1718

1819
// EIDRHDeserializer returns enrollment IDs behind the owners of token
1920
type EIDRHDeserializer struct {
21+
mutex sync.RWMutex
2022
deserializers map[identity.Type]driver2.AuditInfoDeserializer
2123
}
2224

@@ -28,6 +30,8 @@ func NewEIDRHDeserializer() *EIDRHDeserializer {
2830
}
2931

3032
func (e *EIDRHDeserializer) AddDeserializer(typ identity.Type, d driver2.AuditInfoDeserializer) {
33+
e.mutex.Lock()
34+
defer e.mutex.Unlock()
3135
e.deserializers[typ] = d
3236
}
3337

@@ -69,7 +73,9 @@ func (e *EIDRHDeserializer) DeserializeAuditInfo(ctx context.Context, id driver.
6973
if err != nil {
7074
return nil, errors.Wrap(err, "failed to unmarshal to TypedIdentity")
7175
}
76+
e.mutex.RLock()
7277
d, ok := e.deserializers[si.Type]
78+
e.mutex.RUnlock()
7379
if !ok {
7480
return nil, errors.Errorf("no deserializer found for [%v]", si.Type)
7581
}

token/services/identity/deserializer/signer.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ package deserializer
99
import (
1010
"context"
1111
errors2 "errors"
12+
"slices"
13+
"sync"
1214

1315
"github.com/LFDT-Panurus/panurus/token/driver"
1416
"github.com/LFDT-Panurus/panurus/token/services/identity"
@@ -20,6 +22,7 @@ import (
2022
type TypedSignerDeserializer = idriver.TypedSignerDeserializer
2123

2224
type TypedSignerDeserializerMultiplex struct {
25+
mutex sync.RWMutex
2326
deserializers map[idriver.IdentityType][]TypedSignerDeserializer
2427
}
2528

@@ -28,6 +31,8 @@ func NewTypedSignerDeserializerMultiplex() *TypedSignerDeserializerMultiplex {
2831
}
2932

3033
func (v *TypedSignerDeserializerMultiplex) AddTypedSignerDeserializer(typ idriver.IdentityType, d idriver.TypedSignerDeserializer) {
34+
v.mutex.Lock()
35+
defer v.mutex.Unlock()
3136
_, ok := v.deserializers[typ]
3237
if !ok {
3338
v.deserializers[typ] = []TypedSignerDeserializer{d}
@@ -42,8 +47,10 @@ func (v *TypedSignerDeserializerMultiplex) DeserializeSigner(ctx context.Context
4247
if err != nil {
4348
return nil, errors.Wrap(err, "failed to unmarshal to TypedIdentity")
4449
}
45-
dess, ok := v.deserializers[si.Type]
46-
if !ok {
50+
v.mutex.RLock()
51+
dess := slices.Clone(v.deserializers[si.Type])
52+
v.mutex.RUnlock()
53+
if dess == nil {
4754
return nil, errors.Errorf("no deserializer found for [%v]", si.Type)
4855
}
4956
logger.DebugfContext(ctx, "deserializing [%s] with type [%v]", logging.Base64(id), si.Type)

token/services/identity/deserializer/verifier.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ package deserializer
99
import (
1010
"context"
1111
errors2 "errors"
12+
"slices"
13+
"sync"
1214

1315
"github.com/LFDT-Panurus/panurus/token/driver"
1416
"github.com/LFDT-Panurus/panurus/token/services/identity"
@@ -23,6 +25,7 @@ var logger = logging.MustGetLogger()
2325
type TypedVerifierDeserializer = idriver.TypedVerifierDeserializer
2426

2527
type TypedVerifierDeserializerMultiplex struct {
28+
mutex sync.RWMutex
2629
deserializers map[idriver.IdentityType][]idriver.TypedVerifierDeserializer
2730
}
2831

@@ -31,6 +34,8 @@ func NewTypedVerifierDeserializerMultiplex() *TypedVerifierDeserializerMultiplex
3134
}
3235

3336
func (v *TypedVerifierDeserializerMultiplex) AddTypedVerifierDeserializer(typ idriver.IdentityType, d TypedVerifierDeserializer) {
37+
v.mutex.Lock()
38+
defer v.mutex.Unlock()
3439
_, ok := v.deserializers[typ]
3540
if !ok {
3641
v.deserializers[typ] = []TypedVerifierDeserializer{d}
@@ -45,8 +50,10 @@ func (v *TypedVerifierDeserializerMultiplex) DeserializeVerifier(ctx context.Con
4550
if err != nil {
4651
return nil, errors.Wrap(err, "failed to unmarshal to TypedIdentity")
4752
}
48-
dess, ok := v.deserializers[si.Type]
49-
if !ok {
53+
v.mutex.RLock()
54+
dess := slices.Clone(v.deserializers[si.Type])
55+
v.mutex.RUnlock()
56+
if dess == nil {
5057
return nil, errors.Errorf("no deserializer found for [%v]", si.Type)
5158
}
5259
logger.DebugfContext(ctx, "deserializing [%s] with type [%v]", id, si.Type)
@@ -73,8 +80,10 @@ func (v *TypedVerifierDeserializerMultiplex) Recipients(id driver.Identity) ([]d
7380
if err != nil {
7481
return nil, errors.Wrap(err, "failed to unmarshal to TypedIdentity")
7582
}
76-
dess, ok := v.deserializers[si.Type]
77-
if !ok {
83+
v.mutex.RLock()
84+
dess := slices.Clone(v.deserializers[si.Type])
85+
v.mutex.RUnlock()
86+
if dess == nil {
7887
return nil, errors.Errorf("no deserializer found for [%v]", si.Type)
7988
}
8089

@@ -110,8 +119,10 @@ func (v *TypedVerifierDeserializerMultiplex) GetAuditInfoMatcher(ctx context.Con
110119
}
111120

112121
func (v *TypedVerifierDeserializerMultiplex) getMatcher(ctx context.Context, idType idriver.IdentityType, id driver.Identity, auditInfo []byte) (driver.Matcher, error) {
113-
dess, ok := v.deserializers[idType]
114-
if !ok {
122+
v.mutex.RLock()
123+
dess := slices.Clone(v.deserializers[idType])
124+
v.mutex.RUnlock()
125+
if dess == nil {
115126
return nil, errors.Errorf("no deserializer found for [%v]", idType)
116127
}
117128

@@ -153,8 +164,10 @@ func (v *TypedVerifierDeserializerMultiplex) GetAuditInfo(ctx context.Context, i
153164
if err != nil {
154165
return nil, errors.Wrap(err, "failed to unmarshal to TypedIdentity")
155166
}
156-
dess, ok := v.deserializers[si.Type]
157-
if !ok {
167+
v.mutex.RLock()
168+
dess := slices.Clone(v.deserializers[si.Type])
169+
v.mutex.RUnlock()
170+
if dess == nil {
158171
return nil, errors.Errorf("no deserializer found for [%v]", si.Type)
159172
}
160173
var errs []error

0 commit comments

Comments
 (0)