Skip to content

Address FileThreadPool race condition #69

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

Merged
merged 1 commit into from
Jan 4, 2016
Merged

Conversation

wfleming
Copy link
Contributor

@wfleming wfleming commented Jan 3, 2016

I saw a couple ThreadErrors around popping from an empty queue in
bugsnag: this got introduced when I moved away from catching all
ThreadErrors. There's a race condition between checking that the queue
is empty & attempting to pop from it.

This change locks the pop operation with a mutex to prevent the race.

👀 @codeclimate/review

@wfleming wfleming force-pushed the will/thread-pool-race branch from 853d256 to e4a14bb Compare January 4, 2016 01:35
yield item
until queue.empty?
item = lock.synchronize { queue.pop(true) unless queue.empty? }
yield item if item
Copy link
Contributor

Choose a reason for hiding this comment

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

WDYT of a small utility method:

# yield item while (item = next_item(queue))

def next_item(queue)
  lock = Mutex.new
  lock.synchronize { !queue.empty? && queue.pop(true) }
end

Another option would be rescue the non-block-empty-queue-popped exception (assuming it's sufficiently specific), and replace it with a nil.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

WDYT of a small utility method:

That sounds good to me, but I think the lock would need to be a member var, yeah? Getting a new lock on every method call doesn't seem real helpful.

Another option would be rescue the non-block-empty-queue-popped exception (assuming it's sufficiently specific)

It's a ThreadError, and I felt that was too broad to rescue: previously we were rescuing them in the entire thread block. I could wrap just the pop with a rescue, but I'm not confident enough about JRuby threading internals to be sure nothing else could raise that exception class at any given time.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the lock would need to be a member var, yeah? Getting a new lock on every method call doesn't seem real helpful.

Can you expand on that for my own education? I'm not familiar with this and assumed it didn't matter from where you instantiated the Mutex, it would always work the same.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh I see. It's not a global lock, you have to use the same instance in both (all) threads for it to actually prevent the concurrency. Sorry about that.

Instance var seems fine. The FP purist in me would probably pass it into #next_item, but this is rubby so, eh.

@wfleming wfleming force-pushed the will/thread-pool-race branch 2 times, most recently from e9526d4 to 8df5299 Compare January 4, 2016 15:50
I saw a couple ThreadErrors around popping from an empty queue in
bugsnag: this got introduced when I moved away from catching all
ThreadErrors. There's a race condition between checking that the queue
is empty & attempting to pop from it.

This change locks the pop operation with a mutex to prevent the race.
@wfleming wfleming force-pushed the will/thread-pool-race branch from 8df5299 to 625a2d6 Compare January 4, 2016 15:53
@wfleming
Copy link
Contributor Author

wfleming commented Jan 4, 2016

@pbrisbin Thanks for that, updated.

(FWIW the single line version of the loop failed to parse: I think it's because yield is special.)

@pbrisbin
Copy link
Contributor

pbrisbin commented Jan 4, 2016

I think it's just the paren-less method call. yield(item) while ... would've probably worked (among other incantations).

Anyway, I like the multi-line way. LGTM.

wfleming added a commit that referenced this pull request Jan 4, 2016
Address FileThreadPool race condition
@wfleming wfleming merged commit 2974c08 into master Jan 4, 2016
@wfleming wfleming deleted the will/thread-pool-race branch January 4, 2016 18:35
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.

2 participants