-
Notifications
You must be signed in to change notification settings - Fork 418
Adds counting semaphores #206
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
Adds counting semaphores #206
Conversation
@adamruzicka Thank you very much for working on this. Your code looks fantastic! I really appreciate you working on both the Ruby and Java versions and also writing great yarddoc. I will try to look at this more deeply and answer your questions as soon as possible. Our company holiday party is tomorrow night so if I'm not able to get you feedback tonight it probably won't be until this weekend. I apologize for the possible delay. |
@jdantonio Thank you. No need to hurry, enjoy the party. I'm sorry for the Java tests failing, I'll try to resolve it as soon as possible. |
# @return [Integer] | ||
def drain_permits | ||
@mutex.synchronize do | ||
@free.tap { |_| @free = 0 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this line be simply @free = 0
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to make it consistent with the Java api and it should return the number of drained permits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that @free = 0
will return 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're right 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possible minor optimization removing the block:
@mutex.synchronize { old, @free = @free, 0 }
return old
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pitr-ch I believe that in your example old
needs to be declared outside the block else its scope will only be within the block:
def drain_permits
old = 0
@mutex.synchronize { old, @free = @free, 0 }
old
end
@adamruzicka If you have a free moment over the next few days, can you please test these variations? I know it may seem somewhat pedantic, but we've learned that these minor variations can often hide troubling concurrency bugs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdantonio yep, you are right, I've overlooked it. Functional alternative:
def drain_permits
old, _ = @mutex.synchronize { old, @free = @free, 0 }
old
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did some testing of the variations[1], the tap one seems to perform slightly better than the others. Is there anything wrong with using tap?
[1] http://pastie.org/9779681
edit: I never did performance testing before so please let me know if this doesn't actually prove anything
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adamruzicka So long as everything happens within the mutex, tap
is fine. I'm surprised that tap
is the fastest variation, but the numbers don't lie. Thank you for humoring us and looking into this!
Timing with a simple clock the way you did is perfectly fine, but you may want to take a look at Ruby's Benchmark class. It provide's more information. You can see how it's used in the couple of benchmark scripts in the examples
folder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did some more testing, see https://gist.github.com/pitr-ch/43fd7c52f01e0cc56cea The tap is slower in the end but only a little. Big surprise for me, thanks @adamruzicka for bringing it up.
@jdantonio Thanks! |
Thank you! We appreciate the help. |
An attempt on implementation of counting semaphores requested in Issue #162 , both mutex and java implementations should be working
Two questions about
reduce_permits
:first, in Java it is a protected method used (according to the docs[1])
in subclasses that use semaphores to track resources that become unavailable
. Is this method needed?second, does it make sense to raise an error if doing the reduce would bring available permits below zero? Another approach would be to reduce available permits to zero if anyone attempted to do this
[1] https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html#reducePermits(int)