Skip to content

Commit 88f2e45

Browse files
wxiaoguanglunny
andauthored
Fix data-race problems in git module (quick patch) (go-gitea#19934)
* Fix data-race problems in git module * use HomeDir instead of setting.RepoRootPath Co-authored-by: Lunny Xiao <[email protected]>
1 parent 23422f9 commit 88f2e45

File tree

8 files changed

+45
-54
lines changed

8 files changed

+45
-54
lines changed

integrations/integration_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
275275
assert.NoError(t, unittest.LoadFixtures())
276276
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
277277
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
278-
assert.NoError(t, git.InitWithConfigSync(context.Background()))
278+
assert.NoError(t, git.InitOnceWithSync(context.Background()))
279279
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
280280
if err != nil {
281281
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
@@ -576,7 +576,7 @@ func resetFixtures(t *testing.T) {
576576
assert.NoError(t, unittest.LoadFixtures())
577577
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
578578
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
579-
assert.NoError(t, git.InitWithConfigSync(context.Background()))
579+
assert.NoError(t, git.InitOnceWithSync(context.Background()))
580580
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
581581
if err != nil {
582582
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)

integrations/migration-test/migration_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func initMigrationTest(t *testing.T) func() {
6262
assert.True(t, len(setting.RepoRootPath) != 0)
6363
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
6464
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
65-
assert.NoError(t, git.InitWithConfigSync(context.Background()))
65+
assert.NoError(t, git.InitOnceWithSync(context.Background()))
6666
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
6767
if err != nil {
6868
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)

models/migrations/migrations_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
203203
deferFn := PrintCurrentTest(t, ourSkip)
204204
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
205205
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
206-
assert.NoError(t, git.InitWithConfigSync(context.Background()))
206+
assert.NoError(t, git.InitOnceWithSync(context.Background()))
207207
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
208208
if err != nil {
209209
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)

models/unittest/testdb.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
117117
if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "integrations", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
118118
fatalTestError("util.CopyDir: %v\n", err)
119119
}
120-
if err = git.InitWithConfigSync(context.Background()); err != nil {
120+
if err = git.InitOnceWithSync(context.Background()); err != nil {
121121
fatalTestError("git.Init: %v\n", err)
122122
}
123123

@@ -202,7 +202,7 @@ func PrepareTestEnv(t testing.TB) {
202202
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
203203
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
204204
assert.NoError(t, CopyDir(metaPath, setting.RepoRootPath))
205-
assert.NoError(t, git.InitWithConfigSync(context.Background()))
205+
assert.NoError(t, git.InitOnceWithSync(context.Background()))
206206

207207
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
208208
assert.NoError(t, err)

modules/git/git.go

+37-41
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,12 @@ var (
3434
GitExecutable = "git"
3535

3636
// DefaultContext is the default context to run git commands in
37-
// will be overwritten by InitWithConfigSync with HammerContext
37+
// will be overwritten by InitXxx with HammerContext
3838
DefaultContext = context.Background()
3939

4040
// SupportProcReceive version >= 2.29.0
4141
SupportProcReceive bool
4242

43-
// initMutex is used to avoid Golang's data race error. see the comments below.
44-
initMutex sync.Mutex
45-
4643
gitVersion *version.Version
4744
)
4845

@@ -131,15 +128,6 @@ func VersionInfo() string {
131128
return fmt.Sprintf(format, args...)
132129
}
133130

134-
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
135-
// This method doesn't change anything to filesystem
136-
func InitSimple(ctx context.Context) error {
137-
initMutex.Lock()
138-
defer initMutex.Unlock()
139-
140-
return initSimpleInternal(ctx)
141-
}
142-
143131
// HomeDir is the home dir for git to store the global config file used by Gitea internally
144132
func HomeDir() string {
145133
if setting.RepoRootPath == "" {
@@ -153,11 +141,9 @@ func HomeDir() string {
153141
return setting.RepoRootPath
154142
}
155143

156-
func initSimpleInternal(ctx context.Context) error {
157-
// at the moment, when running integration tests, the git.InitXxx would be called twice.
158-
// one is called by the GlobalInitInstalled, one is called by TestMain.
159-
// so the init functions should be protected by a mutex to avoid Golang's data race error.
160-
144+
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
145+
// This method doesn't change anything to filesystem. At the moment, it is only used by "git serv" sub-command, no data-race
146+
func InitSimple(ctx context.Context) error {
161147
DefaultContext = ctx
162148

163149
if setting.Git.Timeout.Default > 0 {
@@ -174,33 +160,45 @@ func initSimpleInternal(ctx context.Context) error {
174160
return nil
175161
}
176162

177-
// InitWithConfigSync initializes git module. This method may create directories or write files into filesystem
178-
func InitWithConfigSync(ctx context.Context) error {
179-
initMutex.Lock()
180-
defer initMutex.Unlock()
163+
var initOnce sync.Once
181164

182-
err := initSimpleInternal(ctx)
183-
if err != nil {
184-
return err
185-
}
165+
// InitOnceWithSync initializes git module with version check and change global variables, sync gitconfig.
166+
// This method will update the global variables ONLY ONCE (just like git.CheckLFSVersion -- which is not ideal too),
167+
// otherwise there will be data-race problem at the moment.
168+
func InitOnceWithSync(ctx context.Context) (err error) {
169+
initOnce.Do(func() {
170+
err = InitSimple(ctx)
171+
if err != nil {
172+
return
173+
}
186174

187-
if err = os.MkdirAll(setting.RepoRootPath, os.ModePerm); err != nil {
188-
return fmt.Errorf("unable to create directory %s, err: %w", setting.RepoRootPath, err)
189-
}
175+
// Since git wire protocol has been released from git v2.18
176+
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
177+
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
178+
}
179+
180+
// By default partial clones are disabled, enable them from git v2.22
181+
if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
182+
globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true")
183+
}
190184

191-
if CheckGitVersionAtLeast("2.9") == nil {
192185
// Explicitly disable credential helper, otherwise Git credentials might leak
193-
globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
194-
}
186+
if CheckGitVersionAtLeast("2.9") == nil {
187+
globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
188+
}
195189

196-
// Since git wire protocol has been released from git v2.18
197-
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
198-
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
190+
SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil
191+
})
192+
if err != nil {
193+
return err
199194
}
195+
return syncGitConfig()
196+
}
200197

201-
// By default partial clones are disabled, enable them from git v2.22
202-
if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
203-
globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true")
198+
// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
199+
func syncGitConfig() (err error) {
200+
if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
201+
return fmt.Errorf("unable to create directory %s, err: %w", setting.RepoRootPath, err)
204202
}
205203

206204
// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"
@@ -235,17 +233,15 @@ func InitWithConfigSync(ctx context.Context) error {
235233
}
236234
}
237235

238-
if CheckGitVersionAtLeast("2.29") == nil {
236+
if SupportProcReceive {
239237
// set support for AGit flow
240238
if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil {
241239
return err
242240
}
243-
SupportProcReceive = true
244241
} else {
245242
if err := configUnsetAll("receive.procReceiveRefs", "refs/for"); err != nil {
246243
return err
247244
}
248-
SupportProcReceive = false
249245
}
250246

251247
if runtime.GOOS == "windows" {

modules/git/git_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func testRun(m *testing.M) error {
2828
defer util.RemoveAll(repoRootPath)
2929
setting.RepoRootPath = repoRootPath
3030

31-
if err = InitWithConfigSync(context.Background()); err != nil {
31+
if err = InitOnceWithSync(context.Background()); err != nil {
3232
return fmt.Errorf("failed to call Init: %w", err)
3333
}
3434

modules/indexer/stats/indexer_test.go

-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"code.gitea.io/gitea/models/db"
1414
repo_model "code.gitea.io/gitea/models/repo"
1515
"code.gitea.io/gitea/models/unittest"
16-
"code.gitea.io/gitea/modules/git"
1716
"code.gitea.io/gitea/modules/queue"
1817
"code.gitea.io/gitea/modules/setting"
1918

@@ -30,10 +29,6 @@ func TestMain(m *testing.M) {
3029
}
3130

3231
func TestRepoStatsIndex(t *testing.T) {
33-
if err := git.InitWithConfigSync(context.Background()); !assert.NoError(t, err) {
34-
return
35-
}
36-
3732
assert.NoError(t, unittest.PrepareTestDatabase())
3833
setting.Cfg = ini.Empty()
3934

routers/init.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func GlobalInitInstalled(ctx context.Context) {
102102
log.Fatal("Gitea is not installed")
103103
}
104104

105-
mustInitCtx(ctx, git.InitWithConfigSync)
105+
mustInitCtx(ctx, git.InitOnceWithSync)
106106
log.Info("Git Version: %s (home: %s)", git.VersionInfo(), git.HomeDir())
107107

108108
git.CheckLFSVersion()

0 commit comments

Comments
 (0)