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 race surrounding Client.Rate. #255

Merged
merged 1 commit into from
Dec 30, 2015
Merged

Fix data race surrounding Client.Rate. #255

merged 1 commit into from
Dec 30, 2015

Conversation

dmitshur
Copy link
Member

@dmitshur dmitshur commented Dec 29, 2015

Previously, concurrent calls to Client.Do would have data races writing Client.Rate. Protect it with a mutex.

Resolves #253.

I've been using/testing it for the last week or two, and I haven't spotted any other issues with concurrency.

Technically, this is a ever-so-slightly breaking API change, since Rate was previously an exported struct field, but now it's a method, so callers will need to change client.Rate to client.Rate(). I don't see any other way around this. Aside from exporting the mutex and keeping Rate as is, but that would require the user to know about the mutex and lock it when reading Rate. That's very unfriendly API.

@dmitshur
Copy link
Member Author

So far, it's just a simple patch for client.Rate without any documentation change, because I wanted to ask you first if you'd want to add something like this?

--- a/github/github.go
+++ b/github/github.go
@@ -50,6 +50,9 @@ const (
 )

 // A Client manages communication with the GitHub API.
+//
+// Clients are safe for concurrent use by multiple goroutines, but a single Client
+// shouldn't be used to make calls on behalf of different users.
 type Client struct {
    // HTTP client used to communicate with the API.
    client *http.Client

Also, due to the nature of this PR, it should be reviewed more carefully than a simple GitHub API coverage addition PR.

Previously, concurrent calls to Client.Do would have data races writing
Client.Rate. Protect it with a mutex.

Resolves #253.
// this rate may not always be up-to-date. Call RateLimits() to check the
// current rate.
Rate Rate
rateMu sync.Mutex
Copy link
Collaborator

Choose a reason for hiding this comment

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

any reason not to use a sync.RWMutex here so that Rate() can just perform a read lock?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I chose sync.Mutex over sync.RWMutex here on purpose, because I estimated that it'd be simpler and faster, and hence more appropriate.

I've seen some arguments and articles demonstrating that the more complex read-write locks are often slower than the simpler mutexes for basic use cases:

I was convinced to at least use sync.Mutex by default, and upgrade to sync.RWMutex after doing profiling and ensuring it's actually faster and better.

If you prefer I change this to sync.RWMutex, I can do that.

Copy link
Collaborator

Choose a reason for hiding this comment

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

no, this is fine.

Though I would certainly be curious to see benchmarks of the two, not just for this library but in general. I just wonder how much difference it really makes in practice.

@jabley
Copy link

jabley commented Jan 4, 2016

👍

Thank you, I was just running https://github.com/jabley/project-status and hitting the race condition. Quick update and sorted.

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

Successfully merging this pull request may close these issues.

3 participants