Re: [Development] Default enabling inbound flow control on sockets

2012-02-14 Thread shane.kearns
 -Original Message-
 From: development-bounces+shane.kearns=accenture@qt-project.org
 [mailto:development-bounces+shane.kearns=accenture@qt-project.org]
 On Behalf Of Thiago Macieira
 Sent: 13 February 2012 19:27
 To: development@qt-project.org
 Subject: Re: [Development] Default enabling inbound flow control on
 sockets

 As you said yourself, there's a huge potential for regressions here,
 especially with a limit as low as 64k. So I am unsure whether we should
 do this at all.

 We could apply a high buffer size, like 1 MB. That's probably enough to
 contain most data files being downloaded and yet avoid exploding by
 OOM. On a modern embedded system, an application would need to be
 downloading 50 items in parallel (which means from at least 9 different
 servers) before it started to get into trouble.


I'm also not convinced we should do this yet, but some more thoughts on the 
subject.

When using the high level (QNetworkReply) API, you are dealing with objects.
Intuitively, downloading a 100MB file will allocate 100MB of memory unless you 
take steps to prevent this.
You might get more or less data than you were expecting, if the object on the 
server has changed.
Regressions are likely, because downloading a complete object and then 
processing it would be a common usage.

When using the low level (QTcpSocket) API, you are dealing with a stream.
Intuitively, you would expect the stream applies flow control and rate limiting 
by itself.
If using native apis (either synchronous or asynchronous) on all platforms, 
this is what happens.
The exact amount of buffering in the OS level varies between platforms.
With QTcpSocket, this does not happen, because Qt reads all available data from 
the OS into its own buffers each time the event loop runs socket notifiers.
Regressions are less likely, because socket using code is normally processing 
the stream as it arrives.


Subject to local law, communications with Accenture and its affiliates 
including telephone calls and emails (including content), may be monitored by 
our systems for the purposes of security and the assessment of internal 
compliance with Accenture policy.
__

www.accenture.com

___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] Default enabling inbound flow control on sockets

2012-02-14 Thread Thiago Macieira
On terça-feira, 14 de fevereiro de 2012 17.51.33, shane.kea...@accenture.com
wrote:
 When using the low level (QTcpSocket) API, you are dealing with a stream.
 Intuitively, you would expect the stream applies flow control and rate
 limiting by itself. If using native apis (either synchronous or
 asynchronous) on all platforms, this is what happens. The exact amount of
 buffering in the OS level varies between platforms. With QTcpSocket, this
 does not happen, because Qt reads all available data from the OS into its
 own buffers each time the event loop runs socket notifiers. Regressions are
 less likely, because socket using code is normally processing the stream as
 it arrives.

Agreed. I think this applies more to QTcpSocket than to QNetworkReply.

But I'd still use a higher value for the buffer size. The kernel buffer size on
Windows is 48k and on Linux it can reach 128k. Also note the very common way
that people use QDataStream over QTcpSocket, which is:

void Class::readyReadSlot()
{
QDataStream stream(this);
if (size == -1  bytesAvailable() = sizeof(size))
stream  buffer;
if (size  bytesAvailable())
return;

// read using stream here
}

So the question is: what type is most recommended for the size variable?
What's the average size of the datastream packet?

To be honest, I'd also like to replace QDataStream with stream that did that
understood incomplete data (like QXmlStreamReader does).

--
Thiago Macieira - thiago.macieira (AT) intel.com
  Software Architect - Intel Open Source Technology Center
 Intel Sweden AB - Registration Number: 556189-6027
 Knarrarnäsgatan 15, 164 40 Kista, Stockholm, Sweden


signature.asc
Description: This is a digitally signed message part.
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] Default enabling inbound flow control on sockets

2012-02-13 Thread shane.kearns
You've correctly understood the problem, but the scale is less bad (takes a 
couple of minutes to exhaust 2GB limit of a 32 bit process using a loopback 
connection)

Changing the default would mean behavior is the same as if you called 
setReadBufferSize(65536) in the application. Currently the default is 
setReadBufferSize(0), where 0 has the special meaning of unlimited.

Performance tests should not break, because they are reading as fast as 
possible. There may be a degradation on one side if the writer is faster than 
the reader.

UDP sockets losing data if buffering is exceeded is acceptable (it's supposed 
to be an unreliable service)
I hadn't thought too much about UDP at this point, just TCP.

The readAll issue is with QNetworkReply rather than QTcpSocket.
There are two programming models in use for this:

Simple model:
1. send request
2. wait for the finished signal from the reply
3. call readAll to get the data

Good model:
1. send request
2. each time the reply emits bytesAvailable
2a. call readAll to get the currently buffered data
2b. save / process currently buffered data
3. when finished signal is received, call readAll a final time to get the tail

The simple model tends to be used where the expected data is small.
The good model tends to be used where the data size is unknown (e.g. browsers, 
download managers)

We could give a much larger buffer size for QNetworkReply (e.g. 1MB) to catch 
reasonable small downloads, but this might be harder for developers (as they 
only see the buffer limit rarely)

Yes, QIODevice allows partial writes, but Q*Socket always accepts all the data 
in the current implementation.
I actually think this side is less of a problem, because the developer knows 
what kind of data they will be sending.
Therefore they are in the best position to decide if they need write flow 
control or not
-Original Message-
From: andrew.stanley-jo...@nokia.com [mailto:andrew.stanley-jo...@nokia.com] 
Sent: 11 February 2012 11:36
To: Kearns, Shane
Subject: Re: [Development] Default enabling inbound flow control on sockets


Ok, let me verify I understand the problem:

1. If a Qt app is not processing the data, Sockets continue reading no mater 
what.

2. This results in no flow control, and on a 10gigE connection results in the 
Qt app potentially receiving ~1 gigabyte/sec stream of data. (assuming the 
HW/SW etc can handle it)  

3. Qt app exhausts all memory within a couple of seconds.

This seems like relatively brain dead behaviour.  On large systems it's a 
problem, and on smaller devices outright dangerous.

What will changing the default buffer size do?  I assume you mean set a 
reasonable default to QAbstractSocket::readBufferSize().  This seems sensible, 
it will stop the stream and use appropriate flow control, if available with 
that protocol.

1. Break performance tests.

2. datagram (UDP) sockets will lose data.

3. As you said, readAll() may have problems, but it's also incompatible with 
sane network programming.  How can I trust the other side is only going to send 
50 bytes and not 4 gigs?

There's no requirement for write buffering as well.  Under Unix's can you can 
write data to a socket and have it not block and/or have it write less data 
than available.  QIODevice accounts for this and write can return 0 bytes 
written. You can easily argue most programs don't handle this well.  But at 
some point network links can't handle traffic at any arbitrary rate and flow 
control must kick in.  Buffering is a short term solution.

I think it's a good idea to have a build in limit.  I think it avoids easily 
made mistake by developers.

-Andrew

On 10/02/2012, at 8:25 PM, ext shane.kea...@accenture.com wrote:

 The current default behaviour of Qt sockets is that they allocate an 
 unbounded amount of memory if the application is not reading all data from 
 the socket but the event loop is running.
 In the worst case, this causes memory allocation failure resulting in a crash 
 or app exit due to bad_alloc exception
 
 We could change the default read buffer size from 0 (unbounded) to a sensible 
 default value e.g. 64k (to be benchmarked)
 Applications wanting the old behaviour could explicitly call 
 setReadBufferSize(0)
 Applications that already set a read buffer size will be unchanged
 
 The same change would be required at the QNetworkAccessManager level, as 
 there is no point applying flow control at the socket and having unbounded 
 memory allocations in the QNetworkReply.
 
 This is a behavioural change that would certainly cause regressions.
 e.g. any application that waits for the QNetworkReply::finished() signal and 
 then calls readAll() would break if the size of the object being downloaded 
 is larger than the buffer size.
 
 On the other hand, we can't enable default outbound flow control in sockets 
 (we don't want write to block).
 Applications need to use the bytesWritten signal.
 QNetworkAccessManager already implements outbound flow

Re: [Development] Default enabling inbound flow control on sockets

2012-02-13 Thread marius.storm-olsen
On 02/13/2012 08:10 AM, ext shane.kea...@accenture.com wrote:
 Not sure. Is it a big problem? Or is it better to just continue as
 is, and let the applications that do have a problem set it to
 something reasonable to them instead?

 I'd probably suggest that we instead improve the output on that
 worst case failure to help devs fix the problems in their
 programs.

 $0.02

 Ben

 qWarning allocates memory, so once the worst case happens, we can't
 give any output (unless we first handle the exceptions inside
 Q*Socket) It would be possible to print a warning when buffering
 exceeds some threshold we consider to be unreasonably large. We can
 also improve the documentation (it's not bad, but doesn't mention
 flow control explicitly)

 https://bugreports.qt-project.org/browse/QTBUG-14464 seems to be
 explained by missing flow control causing out of memory errors under
 load.

Qt is not known for handling OOM cases, and we actually argue for the OS 
to handle it, and that the application (and its libraries) shouldn't 
have to think about memory not being available.
(Too many error conditions and OOM handling all over the place to bring 
any real benefit for it anyways.)

Here you are suggesting a behavior change to handle an OOM case, where 
the behavior will most likely cause valid applications to stop working, 
since they don't handle a (new and artificial) 64k in-buffer limit?

Maybe we should rather allocate a buffer for qWarning output, allowing 
us to properly report an error condition before the application aborts?

^shrug^ but I rather not have applications start failing at random due 
to a failure to have a new limit.

-- 
.marius
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] Default enabling inbound flow control on sockets

2012-02-13 Thread Olivier Goffart
On Monday 13 February 2012 18:33:51 marius.storm-ol...@nokia.com wrote:
 On 02/13/2012 08:10 AM, ext shane.kea...@accenture.com wrote:
  Not sure. Is it a big problem? Or is it better to just continue as
  is, and let the applications that do have a problem set it to
  something reasonable to them instead?
  
  I'd probably suggest that we instead improve the output on that
  worst case failure to help devs fix the problems in their
  programs.
  
  $0.02
  
  Ben
  
  qWarning allocates memory, so once the worst case happens, we can't
  give any output (unless we first handle the exceptions inside
  Q*Socket) It would be possible to print a warning when buffering
  exceeds some threshold we consider to be unreasonably large. We can
  also improve the documentation (it's not bad, but doesn't mention
  flow control explicitly)
  
  https://bugreports.qt-project.org/browse/QTBUG-14464 seems to be
  explained by missing flow control causing out of memory errors under
  load.
 
 Qt is not known for handling OOM cases, and we actually argue for the OS
 to handle it, and that the application (and its libraries) shouldn't
 have to think about memory not being available.
 (Too many error conditions and OOM handling all over the place to bring
 any real benefit for it anyways.)
 
 Here you are suggesting a behavior change to handle an OOM case, where
 the behavior will most likely cause valid applications to stop working,
 since they don't handle a (new and artificial) 64k in-buffer limit?
 
 Maybe we should rather allocate a buffer for qWarning output, allowing
 us to properly report an error condition before the application aborts?
 
 ^shrug^ but I rather not have applications start failing at random due
 to a failure to have a new limit.

Here the problem is not really OOM, but bad putting way too much data 
structure into memory.

QByteArray is not designed to contain huge data (2GiB is really the maximum, 
only because we use int for indexes)

and stuff like readAll() will crash if that is hapenning.

The second problem seems to be that QTCPSocket buffers everything, it does not 
matters if the program does anything with it or just let the event loop spin.
If QTCPSocket did not buffer, the OS would buffer a bit, then drop the packet, 
so sender's TCP algorithms would reduce the rate of sending letting the time 
for the application to process.  

This is my naïve interpretation of the problem and I might be wrong here.
But I think, indeed, the network and IO stack should do something against 
that.


___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] Default enabling inbound flow control on sockets

2012-02-13 Thread marius.storm-olsen
On 02/13/2012 12:33 PM, ext marius.storm-ol...@nokia.com wrote:
 ^shrug^ but I rather not have applications start failing at random due
 to a failure to have a new limit.

..handle a new limit.

-- 
.marius
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] Default enabling inbound flow control on sockets

2012-02-13 Thread Thiago Macieira
On sexta-feira, 10 de fevereiro de 2012 19.25.04, shane.kea...@accenture.com
wrote:
 We could change the default read buffer size from 0 (unbounded) to a
 sensible default value e.g. 64k (to be benchmarked) Applications wanting
 the old behaviour could explicitly call setReadBufferSize(0) Applications
 that already set a read buffer size will be unchanged
[snip]

 This is a behavioural change that would certainly cause regressions.
 e.g. any application that waits for the QNetworkReply::finished() signal and
 then calls readAll() would break if the size of the object being downloaded
 is larger than the buffer size.

As you said yourself, there's a huge potential for regressions here,
especially with a limit as low as 64k. So I am unsure whether we should do
this at all.

We could apply a high buffer size, like 1 MB. That's probably enough to contain
most data files being downloaded and yet avoid exploding by OOM. On a modern
embedded system, an application would need to be downloading 50 items in
parallel (which means from at least 9 different servers) before it started to
get into trouble.

--
Thiago Macieira - thiago.macieira (AT) intel.com
  Software Architect - Intel Open Source Technology Center
 Intel Sweden AB - Registration Number: 556189-6027
 Knarrarnäsgatan 15, 164 40 Kista, Stockholm, Sweden


signature.asc
Description: This is a digitally signed message part.
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


[Development] Default enabling inbound flow control on sockets

2012-02-10 Thread shane.kearns
The current default behaviour of Qt sockets is that they allocate an unbounded 
amount of memory if the application is not reading all data from the socket but 
the event loop is running.
In the worst case, this causes memory allocation failure resulting in a crash 
or app exit due to bad_alloc exception

We could change the default read buffer size from 0 (unbounded) to a sensible 
default value e.g. 64k (to be benchmarked)
Applications wanting the old behaviour could explicitly call 
setReadBufferSize(0)
Applications that already set a read buffer size will be unchanged

The same change would be required at the QNetworkAccessManager level, as there 
is no point applying flow control at the socket and having unbounded memory 
allocations in the QNetworkReply.

This is a behavioural change that would certainly cause regressions.
e.g. any application that waits for the QNetworkReply::finished() signal and 
then calls readAll() would break if the size of the object being downloaded is 
larger than the buffer size.

On the other hand, we can't enable default outbound flow control in sockets (we 
don't want write to block).
Applications need to use the bytesWritten signal.
QNetworkAccessManager already implements outbound flow control for uploads.

Is making applications safe against this kind of overflow by default worth the 
compatibility breaks?



Subject to local law, communications with Accenture and its affiliates 
including telephone calls and emails (including content), may be monitored by 
our systems for the purposes of security and the assessment of internal 
compliance with Accenture policy.
__

www.accenture.com

___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development


Re: [Development] Default enabling inbound flow control on sockets

2012-02-10 Thread BRM
 From: shane.kea...@accenture.com shane.kea...@accenture.com
T he current default behaviour of Qt sockets is that they allocate an 
unbounded 
 amount of memory if the application is not reading all data from the socket 
 but 
 the event loop is running.
 In the worst case, this causes memory allocation failure resulting in a crash 
 or 
 app exit due to bad_alloc exception
 
 We could change the default read buffer size from 0 (unbounded) to a sensible 
 default value e.g. 64k (to be benchmarked)
 Applications wanting the old behaviour could explicitly call 
 setReadBufferSize(0)
 Applications that already set a read buffer size will be unchanged
 
 The same change would be required at the QNetworkAccessManager level, as 
 there 
 is no point applying flow control at the socket and having unbounded memory 
 allocations in the QNetworkReply.
 
 This is a behavioural change that would certainly cause regressions.
 e.g. any application that waits for the QNetworkReply::finished() signal and 
 then calls readAll() would break if the size of the object being downloaded 
 is 
 larger than the buffer size.
 
 On the other hand, we can't enable default outbound flow control in sockets 
 (we don't want write to block).
 Applications need to use the bytesWritten signal.
 QNetworkAccessManager already implements outbound flow control for uploads.
 
 Is making applications safe against this kind of overflow by default worth 
 the 
 compatibility breaks?

Not sure. Is it a big problem? Or is it better to just continue as is, and let 
the applications that do have a problem set it to something reasonable to them 
instead?

I'd probably suggest that we instead improve the output on that worst case 
failure to help devs fix the problems in their programs.

$0.02

Ben
___
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development