classification
Title: socket.settimeout(5.0) does not have any effect
Type: behavior Stage:
Components: IO, Library (Lib) Versions: Python 3.5, Python 3.4, Python 2.7
process
Status: closed Resolution: third party
Dependencies: Superseder:
Assigned To: Nosy List: neologix, piotrjurkiewicz, pitrou
Priority: normal Keywords:

Created on 2015-01-30 01:28 by piotrjurkiewicz, last changed 2015-02-05 07:37 by neologix. This issue is now closed.

Files
File name Uploaded Description Edit
test_unix_sock_timeout.py neologix, 2015-01-30 08:03
Messages (6)
msg235013 - (view) Author: Piotr Jurkiewicz (piotrjurkiewicz) * Date: 2015-01-30 01:28
After setting socket.settimeout(5.0), socket.send() returns immediately, instead of returning after specified timeout.

Steps to reproduce:

Open two python interpreters.

In the first one (the receiver) execute:

>>> import socket
>>> r = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
>>> r.bind("test.sock")

In the second one (the sender) execute:

>>> import socket
>>> s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)

Then run the following command 11 times:

>>> s.sendto("msg", "test.sock")

On the 12 run command will block. This happens because datagram sockets queue on Linux is 11 messages long. Interrupt the command.

So far so good.

Then set sender socket timeout:

>>> s.settimeout(5.0)

Expected behavior:

s.sendto() should block for a 5 seconds and THEN raise error 11 (EAGAIN/EWOULDBLOCK).

Actual behavior:

s.sendto() raises the error IMMEDIATELY.

>>> s.sendto("msg", "test.sock")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
socket.error: [Errno 11] Resource temporarily unavailable

So, in fact, s.settimeout(5.0) does not have any effect.

I think that problem is that settimeout() sets the socket to the non-blocking mode (docs say: "Timeout mode internally sets the socket in non-blocking mode.").

As described [here](http://stackoverflow.com/q/13556972/2178047) setting timeout on non-blocking sockets is impossible.

In fact, when I set timeout manually with setsockopt(), everything works as expected:

>>> s.setblocking(1)              #go back to blocking mode
>>> tv = struct.pack("ll", 5, 0)
>>> s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, tv)

Now s.sendto() raises the error after 5 seconds, as expected.
msg235015 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2015-01-30 01:34
The way socket timeouts are implemented is by using select() to determine whether the socket is ready for read/write. In this case, select() probably marks the socket ready even though the queue is full, which later raises EAGAIN.

About SO_SNDTIMEO and SO_RCVTIMEO, POSIX says "it is implementation-defined whether the SO_SNDTIMEO option can be set". Also, they would not necessarily apply to other operations such as accept().
msg235028 - (view) Author: Charles-François Natali (neologix) * (Python committer) Date: 2015-01-30 08:03
> The way socket timeouts are implemented is by using select() to determine whether the socket is ready for read/write. In this case, select() probably marks the socket ready even though the queue is full, which later raises EAGAIN.

Indeed, and this looks like a kernel bug.

Working as expected on a RHEL6:

$ python /tmp/test_unix_sock_timeout.py
('sending ', 0)
took 0.000s
('sending ', 1)
took 0.000s
('sending ', 2)
took 0.000s
('sending ', 3)
took 0.000s
('sending ', 4)
took 0.000s
('sending ', 5)
took 0.000s
('sending ', 6)
took 0.000s
('sending ', 7)
took 0.000s
('sending ', 8)
took 0.000s
('sending ', 9)
took 0.000s
('sending ', 10)
took 0.000s
('sending ', 11)
took 1.000s
Traceback (most recent call last):
  File "/tmp/test_unix_sock_timeout.py", line 17, in <module>
    s.sendto("hello", SOCKNAME)
socket.timeout: timed out

> About SO_SNDTIMEO and SO_RCVTIMEO, POSIX says "it is implementation-defined whether the SO_SNDTIMEO option can be set". Also, they would not necessarily apply to other operations such as accept().

Exactly, the current way timeouts are implemented ar The Right Way, IMO.
msg235044 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2015-01-30 17:31
The test script works on Ubuntu 14.10 as well.
msg235416 - (view) Author: Piotr Jurkiewicz (piotrjurkiewicz) * Date: 2015-02-05 01:52
Does not work on Debian 7 Wheezy, kernel 3.2.65.

$ python test.py
('sending ', 0)
took 0.000s
('sending ', 1)
took 0.000s
('sending ', 2)
took 0.000s
('sending ', 3)
took 0.000s
('sending ', 4)
took 0.000s
('sending ', 5)
took 0.000s
('sending ', 6)
took 0.000s
('sending ', 7)
took 0.000s
('sending ', 8)
took 0.000s
('sending ', 9)
took 0.000s
('sending ', 10)
took 0.000s
('sending ', 11)
took 0.000s
Traceback (most recent call last):
  File "test.py", line 17, in <module>
    s.sendto("hello", SOCKNAME)
socket.error: [Errno 11] Resource temporarily unavailable

$ uname -a
Linux 3.2.0-4-amd64 #1 SMP Debian 3.2.65-1+deb7u1 x86_64 GNU/Linux
msg235422 - (view) Author: Charles-François Natali (neologix) * (Python committer) Date: 2015-02-05 07:37
It's a kernel bug closing (working fine on my Debian wheezy with a more recent kernel BTW).
History
Date User Action Args
2015-02-05 07:37:20neologixsetstatus: open -> closed
resolution: third party
messages: + msg235422
2015-02-05 01:52:11piotrjurkiewiczsetstatus: pending -> open

messages: + msg235416
2015-02-02 21:31:34neologixsetstatus: open -> pending
2015-01-30 17:31:40pitrousetmessages: + msg235044
2015-01-30 08:03:13neologixsetfiles: + test_unix_sock_timeout.py

messages: + msg235028
2015-01-30 01:34:42pitrousetnosy: + neologix, pitrou

messages: + msg235015
versions: - Python 3.2, Python 3.3, Python 3.6
2015-01-30 01:28:12piotrjurkiewiczcreate