Exception "Non-negative number required.Parameter name: count"

May 29, 2009 at 6:33 AM
Edited May 29, 2009 at 6:36 AM

I am getting an exception when I tried load testing  the server using http://fwptt.sourceforge.net/ from two machines simultaneously(10 Threads from each machine).

 

Exception is from HttpClientContext.cs  class at line Buffer.BlockCopy(_buffer, offset, _buffer, 0, _bytesLeft - offset) in OnReceive Method.

Exception message is ""Non-negative number required.Parameter name: count"

Stack trace

   at System.Buffer.BlockCopy(Array src, Int32 srcOffset, Array dst, Int32 dstOffset, Int32 count)
   at HttpServer.HttpClientContext.OnReceive(IAsyncResult ar) in webserver-19633\trunk\HttpServer\HttpClientContext.cs:line 271
   at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
   at System.Net.ContextAwareResult.CompleteCallback(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)

   at

When I debugged, found that _bytesLeft is becoming 0 which resulted in negative number for the count parameter.

Is this because I am sending requests from multiple threads?

 

Thanks,
Rajeesh

http://www.rajeeshcv.com

 

Coordinator
May 29, 2009 at 7:11 AM

I don't think so. Everything should be thread safe unless I've missed something. It's rather something with the packet.

May 29, 2009 at 7:23 AM

Thanks for the reply.

If you don't mind, could you please explain more about how the packet can cause this issues?

Thanks,
Rajeesh

http://www.rajeeshcv.com

Coordinator
May 29, 2009 at 7:39 AM

I don't know. All I know is that it isn't a multithread issue. The last thing done in OnReceive is Stream.BeginRead, which means that the whole buffer have been processed before the next read is initiated. This means that it's impossible for two different threads to access the same buffer.

Jun 1, 2009 at 8:57 AM

I am not sure about this - if it was an issue with the requests itself, then it should be reproducible every time, but for me it is occurring intermittently.


To confirm that HttpClientContext object is accessed from different threads, I logged the call to OnReceive with this line of code


string.Format("object id = {0} \t thread id = {1}", id, Thread.CurrentThread.ManagedThreadId) // id is a variable that holds the static counter value which will be incremented every time the HttpClientContext is created


And in the log I could see that an object with same id is accessed from different threades.

Thanks,
Rajeesh

http://www.rajeeshcv.com

Coordinator
Jun 1, 2009 at 12:16 PM

I never said that it's the same thread that does all the processing for a HttpClientContext. I said that only one thread at a time will access the context (for reads). I'm wrong if you see that two different theads are in OnReceive at the same time.

Coordinator
Jun 1, 2009 at 12:19 PM

A simple test:

1. Add a member in HttpClientContext called _threadId.

2. In the beginning on OnReceive, check if _threadId != 0, then throw an exception

3. Set _threadId to the current threads id.

4. At each exit point in OnReceive, set _threadId to 0.

 

Jun 1, 2009 at 2:06 PM

Thanks for the reply.

I tried with the simple test, and after running few times I could see the "Error when executing concurrently"(A custom message I used for logging) in the log entry , this is code in OnReceive method

            try
            {
#if DEBUG
                if (_threadId != 0)
                {
                    LogWriter.Write(this, LogPrio.Error, "\n Error when executing concurrently \n");
                }
                _threadId = Thread.CurrentThread.ManagedThreadId;
#endif

                int bytesRead = Stream.EndRead(ar);
                if (bytesRead == 0)
                {
                    Disconnect(SocketError.ConnectionReset);
#if DEBUG
                    _threadId = 0;
#endif
                    return;
                }
                _bytesLeft += bytesRead;
                if (_bytesLeft > _buffer.Length)
                {
#if DEBUG
                    throw new BadRequestException("Too large HTTP header: " + Encoding.UTF8.GetString(_buffer, 0, bytesRead));
#else
                    throw new BadRequestException("Too large HTTP header: " + _bytesLeft);
#endif
                }

#if DEBUG
#pragma warning disable 219
                string temp = Encoding.ASCII.GetString(_buffer, 0, _bytesLeft);
                LogWriter.Write(this, LogPrio.Trace, "Received: " + temp);
#pragma warning restore 219
#endif
                int offset = _parser.Parse(_buffer, 0, _bytesLeft);
                if (Stream == null)
                {
#if DEBUG
                    _threadId = 0;
#endif
                    return; // "Connection: Close" in effect.
                }

                // try again to see if we can parse another message (check parser to see if it is looking for a new message)
                int oldOffset = offset;
                while (_parser.CurrentState == RequestParserState.FirstLine && offset != 0 && _bytesLeft - offset > 0)
                {
#if DEBUG
                    temp = Encoding.ASCII.GetString(_buffer, offset, _bytesLeft - offset);
                    LogWriter.Write(this, LogPrio.Trace, "Processing: " + temp);
#endif
                    offset = _parser.Parse(_buffer, offset, _bytesLeft - offset);
                    if (Stream == null)
                    {
#if DEBUG
                        _threadId = 0;
#endif
                        return; // "Connection: Close" in effect.
                    }
                }

                // need to be able to move prev bytes, so restore offset.
                if (offset == 0)
                    offset = oldOffset;

                // copy unused bytes to the beginning of the array
                if (offset > 0 && _bytesLeft != offset)
                    Buffer.BlockCopy(_buffer, offset, _buffer, 0, _bytesLeft - offset);

                _bytesLeft -= offset;
                if (Stream != null && Stream.CanRead)
                    Stream.BeginRead(_buffer, _bytesLeft, _buffer.Length - _bytesLeft, OnReceive, null);
                else
                {
                    _log.Write(this, LogPrio.Warning, "Could not read any more from the socket.");
                    Disconnect(SocketError.Success);
                }
#if DEBUG
                _threadId = 0;
#endif
            }
            catch (BadRequestException err)
            {
                LogWriter.Write(this, LogPrio.Warning, "Bad request, responding with it. Error: " + err);
                try
                {
                    Respond("HTTP/1.0", HttpStatusCode.BadRequest, err.Message);
                }
                catch (Exception err2)
                {
                    LogWriter.Write(this, LogPrio.Fatal, "Failed to reply to a bad request. " + err2);
                }
                Disconnect(SocketError.NoRecovery);
            }
            catch (IOException err)
            {
                LogWriter.Write(this, LogPrio.Debug, "Failed to end receive: " + err.Message);
                if (err.InnerException is SocketException)
                    Disconnect((SocketError)((SocketException)err.InnerException).ErrorCode);
                else
                    Disconnect(SocketError.ConnectionReset);
            }
            catch (ObjectDisposedException err)
            {
                LogWriter.Write(this, LogPrio.Debug, "Failed to end receive : " + err.Message);
                Disconnect(SocketError.NotSocket);
            }

#if DEBUG
            catch (Exception err)
            {
                LogWriter.Write(this, LogPrio.Fatal, "Reading data from port error " + err);
                Disconnect(SocketError.NoRecovery);
            }
            finally
            {
                _threadId = 0;
            }
#endif

 

 

Thanks,
Rajeesh

http://www.rajeeshcv.com

Dec 11, 2009 at 12:08 AM

I'm having the same problem as well with this error.  Is there a patch or work-around available?

Coordinator
Dec 15, 2009 at 7:22 AM

No, I haven't found anything yet. Contributions are welcome.

Jan 5, 2010 at 6:06 PM
Myriad wrote:

I'm having the same problem as well with this error.  Is there a patch or work-around available?

 I had downloaded the source code and compiled on my machine and have not received the same errors since.  (^-^)