Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix data races in the logger module #1414

Closed
wants to merge 7 commits into from

Conversation

typeless
Copy link
Contributor

Fixes #1412

Copy link
Member

@strk strk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see some locking being added which could be good, but would prefer seeing defer in use rather than manual unlock which might be easier to overlook on early returns.

Makefile Outdated
@@ -93,7 +93,7 @@ install: $(wildcard *.go)
build: $(EXECUTABLE)

$(EXECUTABLE): $(SOURCES)
go build -i -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
go build -race -i -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this belongs here, but feel free to send another PR adding a "debug-build" makefile rule.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I am trying to figure out how to remove it from the commit.

@@ -212,13 +212,13 @@ func (l *Logger) SetLogger(adapter string, config string) error {
// DelLogger removes a logger adapter instance.
func (l *Logger) DelLogger(adapter string) error {
l.lock.Lock()
defer l.lock.Unlock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the defer responsible of a race condition ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Fixed.

@strk
Copy link
Member

strk commented Mar 30, 2017 via email

@tboerger tboerger added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Mar 30, 2017
for _, l := range l.outputs {
if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
fmt.Println("ERROR, unable to WriteMsg:", err)
}
}
l.lock.Unlock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace this with a defer on the line after the .Lock() call

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is a bit of tricky and I am afraid it would cause dead-lock if the select statement is included.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the case block not a scope considered by defer ? Does -race also check for deadlocks ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expected defer is function scoped. Correct me if I am wrong.
I am not sure about deadlocks detection of -race. I suppose it doesn't help.

for _, l := range l.outputs {
l.Flush()
}
l.lock.Unlock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace this with a defer on the line after the .Lock() call

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -297,6 +302,7 @@ func (l *Logger) Close() {
break
}
}
l.lock.Unlock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace this with a defer on the line after the .Lock() call

Copy link
Contributor Author

@typeless typeless Mar 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I have bug here as the lock encloses some synchronization points (is this the correct terminology?).

@strk
Copy link
Member

strk commented Mar 30, 2017 via email

@lunny lunny added this to the 1.2.0 milestone Mar 30, 2017
@lunny lunny added the type/bug label Mar 30, 2017
And locking the shared data structure accurately.
@typeless
Copy link
Contributor Author

typeless commented Mar 30, 2017

I took a closer look into the code and replaced the mutex with an r/w mutex. Also I think protecting the specific data tighter is better.

A wrapper struct for the map might or might not worth the effort and complexity. But if syncmap becomes a thing. we could migrate our shared maps to using it.

Edit: https://godoc.org/golang.org/x/sync/syncmap I haven't figured out how to use it (especially the for ... range part). Hmm, using syncmap might be a good idea.

@strk
Copy link
Member

strk commented Mar 30, 2017

LGTM and you can try the syncmap in a separate PR maybe

if _, ok := l.outputs[mode]; ok {
l.lock.RLock()
_, ok := l.outputs[mode]
l.lock.RUnlock()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this cause a race between checking and removing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct. Fixed.

@@ -174,7 +177,7 @@ type logMsg struct {
// it can contain several providers and log message into all providers.
type Logger struct {
adapter string
lock sync.Mutex
lock sync.RWMutex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the lock for the logger struct or for outputs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think It's for the outputs.

Normally, locks should be per data instead of a chunk of code. And I don't see any other fields except for 'outputs', would be updated concurrently. And channel variables should be thread-safe in its own right.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point was, that reading the code, a lock called lock, with no comments, doesn't make it clear to me that it's a lock for outputs. I would add a comment, or name it outputsLock or something similar. But #1421 is also an option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. Typically the idiomatic way would be to wrap the map in a new type with its own lock. Now that we have syncmap, we can just use it.

@typeless
Copy link
Contributor Author

In favor of #1421

@tboerger tboerger added lgtm/need 1 This PR needs approval from one additional maintainer to be merged. and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Mar 31, 2017
@typeless typeless closed this Mar 31, 2017
@lunny lunny removed this from the 1.2.0 milestone Mar 31, 2017
@typeless typeless deleted the fix-races-of-logger branch April 3, 2019 04:59
@go-gitea go-gitea locked and limited conversation to collaborators Nov 24, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
lgtm/need 1 This PR needs approval from one additional maintainer to be merged. type/bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Panic when the race detector is enabled
5 participants