[Python-Dev] threading.Semaphore()'s counter can become negative for non-ints

T.B. bauertomer at gmail.com
Mon Jan 30 19:40:32 CET 2012

On 2012-01-30 01:46, Victor Stinner wrote:
>> But why would you want to pass a float? It seems like API abuse to me.
> If something should be changed, Semaphore(arg) should raise a
> TypeError if arg is not an integer.
Short version:
I propose the the change to be
-        while self._value == 0:
+        while self._value < 1:
This should not change the flow when Semaphore._value is an int.

Longer explanation:
I thought it is surprising to use math.floor() for threading.Semaphore, 
but now as you propose, we will need to use something like 
int(math.floor(value)) in Python2.x - which is even more surprising. 
That is because math.floor() (and round() for that matter) return a 
float object in Python2.x.

Note: isinstance(4.0, numbers.Integral) is False, even in Python3.x, but 
until now 4.0 was valid as a value for Semaphore(). Also, using the 
builtin int()/math.trunc() on a float is probably not what you want 
here, but rather math.floor().

The value argument given to threading.Semaphore() is really a duck (or 
an object) that can be compared to 0 and 1, incremented by 1 and 
decremented by 1. These are properties that fit float. Why should you 
force the entire builtin int behavior on that object?

I agree that using a float as the counter smells bad, but at times you 
might have something like a fractional resource (which is different from 
a floating point number). In such cases Semaphore.acquire(), after the 
tiny patch above, can be thought as checking if you have at least one 
"unit of resource" available. If you do have at least one such resource 
- acquire it. This will make sure the invariant "The counter can never 
go below zero" holds.


More information about the Python-Dev mailing list