From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM autolearn=unavailable autolearn_force=no version=3.4.4 X-Received: by 10.36.196.135 with SMTP id v129mr2029699itf.36.1478249303138; Fri, 04 Nov 2016 01:48:23 -0700 (PDT) X-Received: by 10.157.52.205 with SMTP id t13mr1485662otd.8.1478249303076; Fri, 04 Nov 2016 01:48:23 -0700 (PDT) Path: eternal-september.org!reader01.eternal-september.org!reader02.eternal-september.org!news.eternal-september.org!news.eternal-september.org!feeder.eternal-september.org!news.glorb.com!n6no837654qtd.0!news-out.google.com!j8ni1329qtc.0!nntp.google.com!p16no837111qta.1!postnews.google.com!glegroupsg2000goo.googlegroups.com!not-for-mail Newsgroups: comp.lang.ada Date: Fri, 4 Nov 2016 01:48:22 -0700 (PDT) In-Reply-To: <4a05ca67-2ecc-4907-a817-38071dba832b@googlegroups.com> Complaints-To: groups-abuse@google.com Injection-Info: glegroupsg2000goo.googlegroups.com; posting-host=2a02:1205:c68c:9d90:717f:db21:6774:b3d3; posting-account=Pm0FhgoAAAAiPscNT3etSZ15tHNZGXm_ NNTP-Posting-Host: 2a02:1205:c68c:9d90:717f:db21:6774:b3d3 References: <4a05ca67-2ecc-4907-a817-38071dba832b@googlegroups.com> User-Agent: G2/1.0 MIME-Version: 1.0 Message-ID: Subject: Re: Gnat Sockets - UDP timeout too short. From: ahlan.marriott@gmail.com Injection-Date: Fri, 04 Nov 2016 08:48:23 +0000 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Xref: news.eternal-september.org comp.lang.ada:32236 Date: 2016-11-04T01:48:22-07:00 List-Id: On Wednesday, 12 October 2016 16:23:39 UTC+2, ah...@marriott.org wrote: > Under Microsoft Windows 8.1 (and later) Gnat.Sockets.Receive_Socket retur= ns too early whilst waiting for a UDP datagram. > In our test program (below) we create a UDP socket, set the timeout for o= ne second, bind it to a port and then call receive on the socket. > We catch and resolve the Socket_Error exception and process the expected = Connection_Timed_Out. > We note the time before issuing Receive_Socket and the time when we catch= the exception and then compare the elapsed time with the receive timeout. > On Window systems prior to Win8.1 this seems to work as expected, the ela= psed time is always greater than the receive timeout. > However under Win8.1 (and later) the call to Receive_Socket returns appro= ximately half a second too early! >=20 > Curiously, if I write the same thing using the Win32.WinSock API then it = works as expected. Which I find odd because I would have thought that Gnat.= Sockets would simply be a series of wrappers around a few WinApi calls. But= then what do I know? >=20 > The effect of this bug is that programs using UDP protocols timeout earli= er than they should do - which often leads to curious behaviour. >=20 > We have tested this on a couple of PCs running a variety of flavours of M= s-Windows. So far it seems that XP & Win7 work as expected whereas Win8 and= Win10 fail. >=20 > Has anyone any idea what might cause this problem and how we might go abo= ut fixing it? >=20 > Best wishes, > MfG > Ahlan >=20 > ------------------------------ > with Ada.Text_IO; > with Ada.Exceptions; > with Ada.Real_Time; > with Ada.Streams; > with GNAT.Sockets; >=20 > package body Test is >=20 > package Io renames Ada.Text_IO; > package Net renames GNAT.Sockets; >=20 > Receive_Timeout : constant Duration :=3D 1.0; >=20 > Receive_Timeout_Span : constant Ada.Real_Time.Time_Span :=3D Ada.Real_T= ime.To_Time_Span (Receive_Timeout); >=20 > procedure Work is > The_Datagram : Ada.Streams.Stream_Element_Array (1..20); > The_Last : Ada.Streams.Stream_Element_Offset; > The_Socket : Net.Socket_Type; > Start_Time : Ada.Real_Time.Time; > End_Time : Ada.Real_Time.Time; > use type Ada.Real_Time.Time; > begin > Net.Create_Socket (Socket =3D> The_Socket, > Family =3D> Net.Family_Inet, > Mode =3D> Net.Socket_Datagram); > Net.Set_Socket_Option (Socket =3D> The_Socket, > Option =3D> (Net.Receive_Timeout, Timeout =3D>= Receive_Timeout)); > Net.Bind_Socket (The_Socket, (Family =3D> Net.Family_Inet, > Addr =3D> Net.Any_Inet_Addr, > Port =3D> 11154)); > loop > begin > Start_Time :=3D Ada.Real_Time.Clock; > Net.Receive_Socket (Socket =3D> The_Socket, > Item =3D> The_Datagram, > Last =3D> The_Last); > Io.New_Line; > Io.Put_Line ("Unexpected reply!"); > exit; > exception > when Occurrence: Net.Socket_Error =3D> > End_Time :=3D Ada.Real_Time.Clock; > declare > Error : constant Net.Error_Type :=3D Net.Resolve_Exception (Occ= urrence); > use type Net.Error_Type; > begin > if Error =3D Net.Connection_Timed_Out then > if End_Time >=3D (Start_Time + Receive_Timeout_Span) then > Io.Put ("."); > else > Io.New_Line; > declare > use type Ada.Real_Time.Time_Span; > Shortfall : constant Ada.Real_Time.Time_Span :=3D Receive= _Timeout_Span - (End_Time - Start_Time); > begin > Io.Put_Line ("Timeout too short by" & Ada.Real_Time.To_Du= ration (Shortfall)'img & "seconds"); > end; > end if; > else > Io.Put_Line ("Socket_Error : Unexpected error=3D" & Error'img= ); > exit; > end if; > end; > when Event : others =3D> > Io.Put_Line ("Unexpected exception: " & Ada.Exceptions.Exception_= Name (Event)); > end; > end loop; > exception > when Event : others =3D> > Io.Put_Line ("Internal Error: " & Ada.Exceptions.Exception_Name (Even= t)); > end Work; To answer my own question... Gnat sockets uses the Winsock function Recv and sets the timeout DWORD SO_R= CVTIMEO in Milliseconds. However according to the Microsoft Developers Network in Feb 2014 (and othe= rs articles) there was an undocumented minimum limit of 500ms which seems t= o have been implemented by Microsoft simply adding 500ms to whatever non-ze= ro value was placed in SO_RECVTIMEO. The consensus workaround was simply to deduct 500ms from the desired timeou= t. This is indeed what Gnat.Sockets seems to have implemented at line 2297 in = g-socket.adb if V4 > 500 then V4 :=3D V4 - 500; elsif v4 > 0 then V4 :=3D 1 endif; At line 1249 in g-socket.adb the 500 is added again if the timeout is retri= eved. It seems to me that recent versions of Windows no longer adds 500ms to the = Recv timeout. This would explain why under Win8 and Win10 our receive of UDP datagrams ti= meout half a second too soon. Gnat.Sockets needs to determine the version of windows and only apply the c= orrection if necessary. The fun of course is going to be finding our which versions of Windows need= the correction and which don=E2=80=99t. ;-) Win8.1 and Win10 don't, Win7 and WinXp do. Can anyone add to this list? Best wishes MfG Ahlan