Skip to content

Commit 9219ec8

Browse files
committed
fix config data race
This fixes a data-race in the config. This does not fix #4942 as there's still a logical race: parallel config updates clobber each other. License: MIT Signed-off-by: Steven Allen <[email protected]>
1 parent 7a4608c commit 9219ec8

File tree

2 files changed

+17
-9
lines changed

2 files changed

+17
-9
lines changed

core/commands/config.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -401,15 +401,18 @@ func transformConfig(configRoot string, configName string, transformer config.Tr
401401
}
402402
defer r.Close()
403403

404-
cfg, err := r.Config()
404+
oldCfg, err := r.Config()
405405
if err != nil {
406406
return nil, nil, err
407407
}
408408

409409
// make a copy to avoid updating repo's config unintentionally
410-
oldCfg := *cfg
411-
newCfg := oldCfg
412-
err = transformer(&newCfg)
410+
newCfg, err := oldCfg.Clone()
411+
if err != nil {
412+
return nil, nil, err
413+
}
414+
415+
err = transformer(newCfg)
413416
if err != nil {
414417
return nil, nil, err
415418
}
@@ -420,13 +423,13 @@ func transformConfig(configRoot string, configName string, transformer config.Tr
420423
return nil, nil, err
421424
}
422425

423-
err = r.SetConfig(&newCfg)
426+
err = r.SetConfig(newCfg)
424427
if err != nil {
425428
return nil, nil, err
426429
}
427430
}
428431

429-
return &oldCfg, &newCfg, nil
432+
return oldCfg, newCfg, nil
430433
}
431434

432435
func getConfig(r repo.Repo, key string) (*ConfigField, error) {

repo/fsrepo/fsrepo.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,11 @@ func (r *FSRepo) Close() error {
476476
return r.lockfile.Close()
477477
}
478478

479+
// Config the current config. This function DOES NOT copy the config. The caller
480+
// MUST NOT modify it without first calling `Clone`.
481+
//
479482
// Result when not Open is undefined. The method may panic if it pleases.
480483
func (r *FSRepo) Config() (*config.Config, error) {
481-
482484
// It is not necessary to hold the package lock since the repo is in an
483485
// opened state. The package lock is _not_ meant to ensure that the repo is
484486
// thread-safe. The package lock is only meant to guard against removal and
@@ -546,11 +548,14 @@ func (r *FSRepo) setConfigUnsynced(updated *config.Config) error {
546548
if err := serialize.WriteConfigFile(configFilename, mapconf); err != nil {
547549
return err
548550
}
549-
*r.config = *updated // copy so caller cannot modify this private config
551+
// Do not use `*r.config = ...`. This will modify the *shared* config
552+
// returned by `r.Config`.
553+
r.config = updated
550554
return nil
551555
}
552556

553-
// SetConfig updates the FSRepo's config.
557+
// SetConfig updates the FSRepo's config. The user must not modify the config
558+
// object after calling this method.
554559
func (r *FSRepo) SetConfig(updated *config.Config) error {
555560

556561
// packageLock is held to provide thread-safety.

0 commit comments

Comments
 (0)