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

Make global loggers safe for concurrent use #316

Merged
merged 2 commits into from
Feb 21, 2017
Merged

Make global loggers safe for concurrent use #316

merged 2 commits into from
Feb 21, 2017

Conversation

akshayjshah
Copy link
Contributor

@akshayjshah akshayjshah commented Feb 18, 2017

This PR adds tests to verify that the global loggers are safe to use and
reconfigure concurrently, and it converts the global variables to (short)
functions.

Users can update their code with a simple gofmt rewrite (which I'll
include in the RC 2 release notes).

@mention-bot
Copy link

@akshayjshah, thanks for your PR! By analyzing the history of the files in this pull request, we identified @peter-edge to be a potential reviewer.

@ansel1
Copy link
Contributor

ansel1 commented Feb 18, 2017

are you sure even writing to a pointer is safely atomic? sync.Value seems to be there specifically for this case.

@akshayjshah
Copy link
Contributor Author

akshayjshah commented Feb 19, 2017

Replacing a pointer is atomic, since it's a single word - readers will see either the old or new value, but not a mix of the two. sync.Value is required for interfaces, which are two words.

I'm certainly not a deep expert here - most of my knowledge comes from Russ Cox's blog posts on Go interfaces and data races.

@bufdev
Copy link
Contributor

bufdev commented Feb 20, 2017

LGTM

Copy link
Collaborator

@prashantv prashantv left a comment

Choose a reason for hiding this comment

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

While writing a pointer is safe on x86 and many other architectures, it may not be safe on all other architectures.

The spec provides no guarantees of this, and reading/writing to a pointer from concurrent goroutines is considered a data race, even if the value is a pointer.

See a couple of golang-nuts threads for more information:
https://groups.google.com/d/msg/golang-nuts/ez9RiiLF0t0/VRIjR5stBQAJ
https://groups.google.com/d/msg/golang-nuts/AULpS8XKDk0/HyzcRloDBgAJ

The data race detector did not flag this since the test has the condition wrong -- it never actually runs the replacement/logging logic.

Once the conditions are flipped, the data race detector should flag this as a race.

global_test.go Outdated
for i := 0; i < 100; i++ {
wg.Add(2)
go func() {
for stop.Load() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

stop is going to false by default, so we're never running this loop, or the loop below. condition needs to be flipped.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, that's embarrassing. 😬

@ansel1
Copy link
Contributor

ansel1 commented Feb 20, 2017

regardless how this works out, this has been an extremely edifying pull request😀

I've been using sync.Value for a similar use case. the performance impact seems to be negligible. like truly negligible. a handful of nanos

@bufdev
Copy link
Contributor

bufdev commented Feb 20, 2017

Ya I can't lie, the links from this PR have pointed out to me how much I've (amateurishly) glossed over some golang details during my time working with the language. Instead of learning about some key details, I did hacks like this https://github.com/peter-edge/pkg-go/blob/master/sync/volatile_bool.go to make sure I wouldn't run into issues, and @prashantv is reminding me why taking shortcuts like this does nothing for your engineering development. I'm starring this PR in my bookmarks.

@akshayjshah akshayjshah changed the title Add a test for concurrent use of global loggers Make global loggers safe for concurrent use Feb 20, 2017
Copy link
Collaborator

@prashantv prashantv left a comment

Choose a reason for hiding this comment

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

Code looks fine -- it's unfortunate that the call site is a little more verbose now.

If we really need to make L and S concurrent safe, then I think this is the right approach. I'd still prefer if the caller set L and S before they started using it to avoid the race.

global_test.go Outdated
"testing"
"time"

"github.com/stretchr/testify/assert"
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: import ordering

global.go Outdated
L = logger
S = logger.Sugar()
return func() { ReplaceGlobals(&prev) }
// This doesn't require synchronization to be safe, since pointers are a
Copy link
Collaborator

Choose a reason for hiding this comment

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

can delete stale comment

Akshay Shah added 2 commits February 20, 2017 19:09
I'd originally intended zap's global loggers to be a consenting-adults sort of
API in which easily-avoided data races are okay. After just a few migrations,
it's become clear just how naive that plan was.

This PR adds tests to verify that the global loggers are safe to use and
reconfigure concurrently, and it converts the global variables to (short)
functions.
@akshayjshah
Copy link
Contributor Author

Agreed about the verbosity of the call sites; I was also hoping that users of this API could just take care to replace the global loggers before they're used, but it's become clear to me that I was hopelessly naive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

5 participants