[Mono-bugs] [Bug 684316] New: WebRequest consistently dies with memory leak (iPhone3G) or Mprotect crash (iPhone4)

bugzilla_noreply at novell.com bugzilla_noreply at novell.com
Thu Mar 31 18:22:07 EDT 2011


https://bugzilla.novell.com/show_bug.cgi?id=684316

https://bugzilla.novell.com/show_bug.cgi?id=684316#c0


           Summary: WebRequest consistently dies with memory leak
                    (iPhone3G) or Mprotect crash (iPhone4)
    Classification: Mono
           Product: MonoTouch
           Version: unspecified
          Platform: iPhone
        OS/Version: All
            Status: NEW
          Severity: Critical
          Priority: P5 - None
         Component: Runtime
        AssignedTo: gnorton at novell.com
        ReportedBy: mike.biz at dussault.org
         QAContact: mono-bugs at lists.ximian.com
          Found By: ---
           Blocker: ---


User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US)
AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16

I've got a MonoTouch app that does a 3.5MB HTTP POST. It seems to leak memory
every time I upload. On an iPhone 3G (OS 3.1.2), the app is jettisoned with a
LowMemory report after 6-10 uploads. On an iPhone 4 (OS 4.2.1), the app gets an
Mprotect error after about 14 uploads.

This is a big showstopper issue for our app because uploading Facebook videos
is a major feature for us.


A few data points:

- Instruments does NOT show the memory increasing as the app continues to
upload.

- Instruments doesn't show any significant leaks (maybe 1 kilobyte total).

- It doesn't matter whether I write the post data in 8k chunks, 64k chunks, or
all at once with one Stream.Write() call.

- It doesn't matter whether I wait for a response (HttpWebRequest.HaveResponse)
or not before starting the next upload.

- It doesn't matter if the POST data is even valid. I've tried using valid POST
data and I've tried sending 3MB of zeros.

- It doesn't matter if I use the async versions of the WebRequest calls from my
main thread vs creating a thread of my own and using the synchronous versions
of the WebRrequest and stream calls.

- If the app is not allocating any data each frame (in SimulateAppAllocations),
then the app still dies eventually but it takes longer to run out of memory.
(BUT: as mentioned before, the memory that I'm allocating each frame is not
referenced after the frame it was allocated on, so it should always be
available for the garbage collector to cleanup).




Reproducible: Always

Steps to Reproduce:
1. Create a new MonoTouch iPhone OpenGL ES project.

2. Paste the following code into the EAGLView class and call UpdateCrashTest()
from OnRenderFrame(). (Note that I've also attached the complete EAGLView.cs
file to this bug report).



void UpdateCrashTest()
        {
            SimulateAppAllocations();
            UpdatePost();
        }



        AsyncHttpPost m_Post;
        int m_nPosts = 1;

        void UpdatePost()
        {
            if ( m_Post == null || m_Post.PostStatus !=
AsyncHttpPostStatus.InProgress )
            {
                System.Console.WriteLine( string.Format( "Starting post {0}",
m_nPosts++ ) );

                m_Post = new AsyncHttpPost(
                    "https://api-video.facebook.com/restserver.php",
                    "multipart/form-data; boundary=" + "8cdbcdf18ab6640" );
            }
        }


        //
        // Stuff to randomly allocate in order to simulate a normal app's
behavior.
        //
        Random m_Random = new Random(0);
        List< byte [] > m_Allocations;
        List< byte[] > m_InitialAllocations;

        void SimulateAppAllocations()
        {
            // First time through, allocate a bunch of data that the app would
allocate.
            // We'll keep our pointers to this stuff so it doesn't get garbage
collected.
            if ( m_InitialAllocations == null )
            {
                m_InitialAllocations = new List<byte[]>();
                int nInitialBytes = 6 * 1024 * 1024;
                int nBlockSize = 30000;
                for ( int nCurBytes = 0; nCurBytes < nInitialBytes; nCurBytes
+= nBlockSize )
                {
                    m_InitialAllocations.Add( new byte[nBlockSize] );
                }
            }

            // Also, each frame, allocate up to 100k of data. We don't hang
onto these pointers after the next
            // frame so it should get garbage collected.
            m_Allocations = new List<byte[]>();
            for ( int i=0; i < 10; i++ )
            {
                int nAllocationSize = m_Random.Next( 10000 ) + 10;
                m_Allocations.Add( new byte[nAllocationSize] );
            }
        }
    }


    public enum AsyncHttpPostStatus
    {
        InProgress,
        Success,
        Fail
    }

    public class AsyncHttpPost
    {
        public AsyncHttpPost( string sURL, string sContentType )
        {
            m_PostStatus = AsyncHttpPostStatus.InProgress;
            m_sContentType = sContentType;
            m_sURL = sURL;

            m_UploadThread = new Thread( new ThreadStart( UploadThread ) );
            m_UploadThread.Start();
        }

        void UploadThread()
        {
            using ( MonoTouch.Foundation.NSAutoreleasePool pool = new
MonoTouch.Foundation.NSAutoreleasePool() )
            {
                try
                {
                    HttpWebRequest request = WebRequest.Create( m_sURL ) as
HttpWebRequest;
                    request.Method = "POST";
                    request.ContentType = m_sContentType;
                    request.CachePolicy = new
System.Net.Cache.RequestCachePolicy(
System.Net.Cache.RequestCacheLevel.NoCacheNoStore );

                    int nBytesToWrite = 1024 * 1024 * 3; // m_PostData.Length;
                    request.ContentLength = nBytesToWrite;

                    // Write the post data.
                    using ( Stream stream = request.GetRequestStream() )
                    {
                        int nBlockSize = 1024 * 8;
                        for ( int nBytesWritten=0; nBytesWritten <
nBytesToWrite; nBytesWritten += nBlockSize )
                        {
                            int nBytes = Math.Min( nBytesToWrite -
nBytesWritten, nBlockSize );
                            byte [] bytes = new byte[nBytes];
                            stream.Write( bytes, 0, bytes.Length );
                        }

                        stream.Close();
                    }

                    // Wait for the response.
                    while ( !request.HaveResponse )
                    {
                        MonoTouch.Foundation.NSThread.SleepFor( 0.1f );
                    }
                    System.Console.WriteLine( "Finished!" );

                    // Finished!
                    m_PostStatus = AsyncHttpPostStatus.Success;
                }
                catch ( System.Exception e )
                {
                    System.Console.WriteLine( "Error in
AsyncHttpPost.UploadThread:\n" + e.Message );
                    m_PostStatus = AsyncHttpPostStatus.Fail;
                }
            }
        }

        public AsyncHttpPostStatus PostStatus
        {
            get
            {
                return m_PostStatus;
            }
        }


        Thread m_UploadThread;
        AsyncHttpPostStatus m_PostStatus;
        string m_sContentType;
        string m_sURL;
    } 
Actual Results:  
iPhone 3G Result: The app gets through 6 to 10 uploads and then the OS kills
it. There is no crash log, but there is a LowMemory log showing that the app
was jettisoned.

iPhone 4 Result: It gets an Mprotect error around the 11th upload.

Expected Results:  
Since we're not keeping references to old objects here, the app should run
forever without crashing or running out of memory.

-- 
Configure bugmail: https://bugzilla.novell.com/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the QA contact for the bug.


More information about the mono-bugs mailing list