Archived

This forum has been archived. Please start a new discussion on GitHub.

IceSL 0.3.3: MemoryStream issue

kwaclaw
kwaclaw Oshawa, Canada
I recently encountered this exception in Ice for Silverlight:
(not asking for support, just reporting it)

Error: Unhandled Error in Silverlight 2 Application Memory stream is not expandable. at System.IO.__Error.MemoryStreamNotExpandable()
at System.IO.MemoryStream.set_Capacity(Int32 value)
at System.IO.MemoryStream.EnsureCapacity(Int32 value)
at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at IceInternal.IceConnection.receiveCompleted(Object sender, SocketAsyncEventArgs e)
at System.Net.Sockets.SocketAsyncEventArgs.OnCompleted(SocketAsyncEventArgs e)
at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSuccess(SocketError socketError, Int32 bytesTransferred, SocketFlags flags)
at System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

I am not sure how I can reliably reproduce it, but as far as I can tell, the ReadCallback in IceConnection.cs class instantiates a non-resizable stream which then throws this exception when the need for expansion arises.

Karl

Comments

  • kwaclaw
    kwaclaw Oshawa, Canada
    kwaclaw wrote: »
    I am not sure how I can reliably reproduce it, but as far as I can tell, the ReadCallback in IceConnection.cs class instantiates a non-resizable stream which then throws this exception when the need for expansion arises.

    Karl

    The issues seeems to happen whenever ReadCallback.prepareWrite() enter the "else" branche of its main "if" statement.

    The code there looks like:
    int length = (int) (_stream.Length - _replySize);
      Byte[] buff = new Byte[length];
      _stream.Position = _replySize;
      _stream.Read(buff, 0, length);
      _stream = new MemoryStream(buff);
      _replySize = 0;
      _stream.Position = length;
    
    This memorystream is not resizable. It can be made so by changing the code:
    int length = (int) (_stream.Length - _replySize);
      MemoryStream ms = new MemoryStream(length);
      byte[] buff = ms.GetBuffer();
      _stream.Position = _replySize;
      _stream.Read(buff, 0, length);
      ms.SetLength(length);
      ms.Position = length;
      _stream = ms;
      _replySize = 0;
    
    However, if this is the solution, it is not complete, as I now get an Ice.BadMagicException:

    Error: Unhandled Error in Silverlight 2 Application Exception of type 'Ice.BadMagicException' was thrown. at Ice.ObjectPrxHelperBase.handleExceptionWrapper__(LocalExceptionWrapper ex)
    at KdSoftIce.Services.WhiteboardService.WhiteboardSessionPrxHelper.AddPoints(Int32 clientId, StylusPoint[] points, Dictionary`2 context__, Boolean explicitContext__)
    at KdSoftIce.Services.WhiteboardService.WhiteboardSessionPrxHelper.AddPoints(Int32 clientId, StylusPoint[] points)
    at WhiteBoardSL.Page.AddPointsTask.DoExecute()
    Source File: http://localhost:4404/WhiteBoardSLTestPage.aspx?host=ts-citxp-kwacla
    Line: 0

    Karl
  • kwaclaw
    kwaclaw Oshawa, Canada
    kwaclaw wrote: »
    However, if this is the solution, it is not complete, as I now get an Ice.BadMagicException:

    Seems my fix was not good - apparently MemoryStream.SetLength clears the stream. Rearranging the order seems to work:
    int length = (int) (_stream.Length - _replySize);
      MemoryStream ms = new MemoryStream();
      ms.SetLength(length);
      byte[] buff = ms.GetBuffer();
      _stream.Position = _replySize;
      _stream.Read(buff, 0, length);
      ms.Position = length;
      _stream = ms;
      _replySize = 0;
    
    So far I could not re-create the error, so the situation seems improved at least.

    Karl
  • xdm
    xdm La Coruña, Spain
    Hi Karl,

    Thanks for the bug report and sorry for the late replay, i'm glad you already found a solution for the issue, as you say the problem is that MemoryStream that are not create with the default constructor cannot be expandable.

    I have made a fix similar to yours but not require to call SetLength
    public void prepareWrite()
        {
            if(_stream == null || _replySize == _stream.Length)
            {
                _replySize = 0;
                _stream = new MemoryStream();
                _stream.Position = 0;
            }
            else
            {
                int length = (int) (_stream.Length - _replySize);
                Byte[] buff = new Byte[length];
                _stream.Position = _replySize;
                _stream.Read(buff, 0, length);
                _stream = new MemoryStream();
                _stream.Write(buff, 0, length);
                _replySize = 0;
            }
        }
    

    Cheers,
    José
  • kwaclaw
    kwaclaw Oshawa, Canada
    xdm wrote: »
    Hi Karl,

    I have made a fix similar to yours but not require to call SetLength
        int length = (int) (_stream.Length - _replySize);
        Byte[] buff = new Byte[length];
        _stream.Position = _replySize;
        _stream.Read(buff, 0, length);
        _stream = new MemoryStream();
        _stream.Write(buff, 0, length);
        _replySize = 0;
    

    That should work as well, only it requires more processing.
    This code will allocate a buffer twice (the second time as side effect of writing to the new stream), and fill it twice (the second time when writing to the new stream).

    I tried to allocate and fill the buffer only once:
        int length = (int) (_stream.Length - _replySize);
        MemoryStream ms = new MemoryStream();
        ms.SetLength(length);            // allocates buffer inside new stream
        byte[] buff = ms.GetBuffer();    // get reference to new buffer
        _stream.Position = _replySize;
        _stream.Read(buff, 0, length);   // fill the new stream's buffer
        ms.Position = length;            // fix up the position
        _stream = ms;
        _replySize = 0;
    

    Karl