Commit b7d5159e by Dmitriy Anisimkov Committed by Pierre-Marie de Rodat

[Ada] GNAT.Sockets: fix socket timeout on recent Windows versions

2019-07-04  Dmitriy Anisimkov  <anisimko@adacore.com>

gcc/ada/

	* doc/gnat_ugn/platform_specific_information.rst: Document
	Windows socket timeout particularity.
	* gnat_ugn.texi: Regenerate.
	* gsocket.h: Include versionhelpers.h.
	* socket.c (__gnat_minus_500ms): New function.
	* libgnat/g-sothco.ads (Minus_500ms_Windows_Timeout): New
	imported function.
	* libgnat/g-socket.adb (Set_Socket_Option): Refactor to remove
	500ms from the requested timeout only on old Windows version.

From-SVN: r273045
parent 1708a783
2019-07-04 Dmitriy Anisimkov <anisimko@adacore.com>
* doc/gnat_ugn/platform_specific_information.rst: Document
Windows socket timeout particularity.
* gnat_ugn.texi: Regenerate.
* gsocket.h: Include versionhelpers.h.
* socket.c (__gnat_minus_500ms): New function.
* libgnat/g-sothco.ads (Minus_500ms_Windows_Timeout): New
imported function.
* libgnat/g-socket.adb (Set_Socket_Option): Refactor to remove
500ms from the requested timeout only on old Windows version.
2019-07-04 Thomas Quinot <quinot@adacore.com>
* get_scos.adb: Remove bogus, dead code.
......
......@@ -488,6 +488,49 @@ and::
Ada.Command_Line.Argument (1) -> "'*.txt'"
Windows Socket Timeouts
-----------------------
Microsoft Windows desktops older than ``8.0`` and Microsoft Windows Servers
older than ``2019`` set a socket timeout 500 milliseconds longer than the value
set by setsockopt with ``SO_RCVTIMEO`` and ``SO_SNDTIMEO`` options. The GNAT
runtime makes a correction for the difference in the corresponding Windows
versions. For Windows Server starting with version ``2019``, the user must
provide a manifest file for the GNAT runtime to be able to recognize that
the Windows version does not need the timeout correction. The manifest file
should be located in the same directory as the executable file, and its file
name must match the executable name suffixed by ``.manifest``. For example,
if the executable name is :file:`sock_wto.exe`, then the manifest file name
has to be :file:`sock_wto.exe.manifest`. The manifest file must contain at
least the following data::
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>
Without the manifest file, the socket timeout is going to be overcorrected on
these Windows Server versions and the actual time is going to be 500
milliseconds shorter than what was set with GNAT.Sockets.Set_Socket_Option.
Note that on Microsoft Windows versions where correction is necessary, there
is no way to set a socket timeout shorter than 500 ms. If a socket timeout
shorter than 500 ms is needed on these Windows versions, a call to
Check_Selector should be added before any socket read or write operations.
.. _Mixed-Language_Programming_on_Windows:
Mixed-Language Programming on Windows
......
......@@ -82,6 +82,7 @@
#ifdef __MINGW32__
#include <winsock2.h>
#include <ws2tcpip.h>
#include <versionhelpers.h>
#undef EACCES
#define EACCES WSAEACCES
......
......@@ -2643,21 +2643,29 @@ package body GNAT.Sockets is
=>
if Is_Windows then
-- On Windows, the timeout is a DWORD in milliseconds, and
-- the actual timeout is 500 ms + the given value (unless it
-- is 0).
-- On Windows, the timeout is a DWORD in milliseconds
U4 := C.unsigned (Option.Timeout / 0.001);
Len := U4'Size / 8;
Add := U4'Address;
if U4 > 500 then
U4 := U4 - 500;
U4 := C.unsigned (Option.Timeout / 0.001);
elsif U4 > 0 then
if Option.Timeout > 0.0 and then U4 = 0 then
-- Avoid round to zero. Zero timeout mean unlimited.
U4 := 1;
end if;
Len := U4'Size / 8;
Add := U4'Address;
-- Old windows versions actual timeout is 500 ms + the given
-- value (unless it is 0).
if Minus_500ms_Windows_Timeout /= 0 then
if U4 > 500 then
U4 := U4 - 500;
elsif U4 > 0 then
U4 := 1;
end if;
end if;
else
VT := To_Timeval (Option.Timeout);
......
......@@ -438,6 +438,11 @@ package GNAT.Sockets.Thin_Common is
renames Short_To_Network;
-- Symmetric operation
function Minus_500ms_Windows_Timeout return C.int;
-- Microsoft Windows desktop older then 8.0 and Microsoft Windows Server
-- older than 2019 need timeout correction for 500 milliseconds. This
-- routine returns 1 for such versions.
private
pragma Import (C, Get_Socket_From_Set, "__gnat_get_socket_from_set");
pragma Import (C, Is_Socket_In_Set, "__gnat_is_socket_in_set");
......@@ -470,4 +475,6 @@ private
pragma Import (C, Hostent_H_Length, "__gnat_hostent_h_length");
pragma Import (C, Hostent_H_Addr, "__gnat_hostent_h_addr");
pragma Import (C, Minus_500ms_Windows_Timeout, "__gnat_minus_500ms");
end GNAT.Sockets.Thin_Common;
......@@ -803,4 +803,15 @@ const char * __gnat_gai_strerror(int errcode) {
#endif
int __gnat_minus_500ms() {
#if defined (_WIN32)
// Windows Server 2019 and Windows 8.0 do not need 500 millisecond socket
// timeout correction.
return !(IsWindows8OrGreater() && !IsWindowsServer()
|| IsWindowsVersionOrGreater(10, 0, 17763));
#else
return 0;
#endif
}
#endif /* defined(HAVE_SOCKETS) */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment