1

I am developing a Windows application that uploads a file to a webserver, IIS. My code is working just fine when I run the app on a 64Bit Machine. Upload to the IIS is working. But when I run it on a 32Bit machine, the upload is not working.

I think it has something to do with IIS. But I don´t know what it could be. Does someone has experienced same issues?

UPDATE: This has nothing to with the server side. I tested several endpoints, but nothing worked.

This must be related to my upload code. This code is working from 64Bit Apps but not on 32Bit:

try
        {
            System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient();
            hc.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/html,application/xhtml+xml,application/xml");
            hc.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate");
            hc.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:19.0) Gecko/20100101 Firefox/19.0");
            hc.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Charset", "ISO-8859-1");

            using (VirtualStream ms = new VirtualStream() { Size = UploadSize })
            {
                StreamContent content = new StreamContent(ms, BufferSize);

                // time for the calculation of the total average throughput
                var overallStart = DateTime.Now;

                var start = DateTime.Now;

                var responseTask = hc.PostAsync(URL, content);

                while (!responseTask.IsCompleted)
                {
                    // Check the Exit and Abort Constraints
                    if ((DateTime.Now - overallStart).TotalMilliseconds > MaxTestLength || _cancelationRequested)
                    {
                        System.Diagnostics.Debug.WriteLine("Bytes sent " + bytesSent);
                        hc.CancelPendingRequests();
                        IsRunning = false;
                        return;
                    }

                    try
                    {
                        bytesSent = ms.Position - bytesOfCalibrationPhase;
                    }
                    catch (Exception)
                    {
                        // The Upload is an async process which dispses the underlying stream when the upload finishes
                        // In some cases this could lead to ObjectDiposed Exceptions when accessing the current stream position
                        // If it is the case, the upload has finished....
                        break;
                    }


                }

BytesSent is always "0" on 32Bit machines...Why is that?

davidOhara
  • 1,008
  • 5
  • 17
  • 39
  • What do you mean by `"the upload is not working"`? Do you get an error on upload? Does it silently fail? Elaborate. – DGibbs Sep 16 '15 at 08:25
  • It just fails silently. No error at all... – davidOhara Sep 16 '15 at 08:26
  • 1
    Have a look at the app pool in IIS, do you have `Enable 32-Bit Applications` set to `true`? What managed pipeline mode are you using? – DGibbs Sep 16 '15 at 08:27
  • @DGibbs Thx for the tip, it was set to false. I set it to true, but it is still not working...I am using the integrated pipeline... – davidOhara Sep 16 '15 at 08:38
  • I remember reading something about compiler differences between 32 and 64bits, and some people experience runtime issues. Although no solution that I can remember. – Tomas Smagurauskas Sep 16 '15 at 08:40
  • 2
    @davidOhara. No error in event viewer as well? – Taleeb Sep 16 '15 at 08:43
  • @davidOhara Can you build a separate version as a 32-bit app? – DGibbs Sep 16 '15 at 08:43
  • @Taleeb No there´s no error. But I am just seeing an exception in my output window:EventSourceException: No Free Buffers available from the operating system (e.g. event rate too fast). – davidOhara Sep 16 '15 at 08:51
  • @DGibbs I can and I am already doing this. But this not solves the error... – davidOhara Sep 16 '15 at 10:06
  • Updated my question! – davidOhara Sep 17 '15 at 10:05
  • IMHO an exception **is** an error. You should look into that. – Thomas Weller Sep 18 '15 at 11:36
  • There is no delay in the loop `while (!responseTask.IsCompleted)`. This will at least result in a 100% CPU loop (1 core). Also, if you're expecting a ObjectDisposedException, why do you catch any Exception? – Thomas Weller Sep 18 '15 at 11:42
  • How big is the file that you are trying to upload? Perhaps your hitting some sort of memory limit – iamkrillin Sep 18 '15 at 12:40
  • @iamkrillin The file size is 150MB and BufferSize is 4096. But I already tried more less values. But same error. – davidOhara Sep 18 '15 at 12:59
  • @Thomas I want to measure the throughput speed. So a delay in that while statement would sophisticate the result, wouldn´t it? – davidOhara Sep 18 '15 at 13:01
  • Running in a very fast loop like that, wouldn't `bytesSent` remain constant for many loops? Data is sent in packets. Packets have sizes of 1k. Sending 1k takes 75us on a 100 MBit connection. Looping faster than that is not required. `DateTime.Now` probably has a precision of the Windows timer anyway, so IMHO you can do a `Thread.Sleep(15)` without problems. – Thomas Weller Sep 18 '15 at 13:46
  • I'd suggest you remove that try catch you have and see what error is getting thrown, or at the very least, log the error instead of eating it. – iamkrillin Sep 18 '15 at 13:53
  • @Thomas Thanks for the tipp, I will def try that. But why is that code without a Sleep working on 64Bit machines? – davidOhara Sep 18 '15 at 15:20
  • @davidOhara, Is the `VirtualStream` class a custom class that you created? – Yacoub Massad Sep 24 '15 at 21:48

2 Answers2

1

It seems to me that the loop you have is there for three reasons:

1) You want to cancel the upload if cancellation is requested

2) You want to cancel the upload if there is a timeout

3) you want to know the progress of the upload

I suggest that you remove the loop completely, and achieve these tree goals in a different way. Here is how you can do this:

For (1), use the other overload of the PostAsync method that has a CancellationToken parameter. This allows you to provide a token that you can use from somewhere else to cancel the upload operation.

For (2), you can use the CancellationTokenSource (that you used to create the CancellationToken), to request that the upload operation be canceled after some time (if the task was not already completed). See the CancelAfter method.

Here is some code sample for (1) and (2):

Put the following two lines in some place (probably as fields) so that these variables are available from both your upload code and the code that might wish to cancel the upload:

CancellationTokenSource cancellation_token_source = new CancellationTokenSource();
CancellationToken cancellation_token = cancellation_token_source.Token;

The following line of code will setup automatic cancellation after 10 seconds:

cancellation_token_source.CancelAfter(TimeSpan.FromSeconds(10));

In the following line, we pass the cancellation_token to the PostAsync method:

var responseTask = hc.PostAsync(URL, content, cancellation_token);

I noticed you were waiting for responseTask.IsCompleted to become true. This means that you don't want your method to return until the upload is complete. In this case, use the following to wait for the upload to complete.

responseTask.Wait();

In case you want to convert your method to become asynchronous, mark your method as async, and instead use the following:

await responseTask;

You can use the following to cancel the upload:

cancellation_token_source.Cancel();

For (3), first look at the answers in this question.

If this does not work for you, I have the following suggestion:

You can create a decorator for the Stream class, that informs you when the stream was read, like this (please note the Read method):

public class ReadNotifierStreamWrapper : Stream
{
    private readonly Stream m_Stream;

    private readonly Action<int> m_ReadNotifier;

    public ReadNotifierStreamWrapper(Stream stream, Action<int> read_notifier)
    {
        m_Stream = stream;
        m_ReadNotifier = read_notifier;
    }

    public override void Flush()
    {
        m_Stream.Flush();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return m_Stream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        m_Stream.SetLength(value);
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        var bytes_read = m_Stream.Read(buffer, offset, count);

        m_ReadNotifier(bytes_read);

        return bytes_read;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        m_Stream.Write(buffer, offset, count);
    }

    public override bool CanRead
    {
        get { return m_Stream.CanRead; }
    }

    public override bool CanSeek
    {
        get { return m_Stream.CanSeek; }
    }

    public override bool CanWrite
    {
        get { return m_Stream.CanWrite; }
    }

    public override long Length
    {
        get { return m_Stream.Length; }
    }

    public override long Position
    {
        get { return m_Stream.Position; }
        set { m_Stream.Position = value; }
    }
}

And then you can use it to wrap your ms stream like this:

int total_bytes = 0;

var stream_wrapper = new ReadNotifierStreamWrapper(ms , bytes =>
{
    total_bytes += bytes;

    Debug.WriteLine("Bytes sent " + total_bytes);
});

HttpContent content = new StreamContent(stream_wrapper); //Here we are creating the StreamContent from stream_wrapper instead of ms

This way you will get a notification when the stream is being read. And you will know how much bytes were read.

Please note that you might need to work more on the ReadNotifierStreamWrapper class to make it better. For example, maybe the HttpClient decides for some reason that it wants to seek the stream via the Seek method. You might need to take that into account. Although, I don't think that HttpClient will do this since it needs to read the whole file to upload it all, it does not make sense to skip parts of the file. You can put some breakpoints on the Seek method of ReadNotifierStreamWrapper to see if it will get called.

Community
  • 1
  • 1
Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62
  • Very good...This helped a lot but the cancellation_token_source.Cancel(); is not working though. I don´t know why yet, but after calling cancel the Stream is still reading bytes... – davidOhara Sep 25 '15 at 08:40
  • So when you call `Cancel`, the upload stops but not immediately? or does it not stop at all? – Yacoub Massad Sep 25 '15 at 10:28
  • How much time between when you ask it to cancel and when it stops? I think that this is the behavior of PostAsync, you ask it to Cancel, then it needs sometime to gracefully stop. – Yacoub Massad Sep 25 '15 at 11:14
  • Yes, I already solved it by if (cancellation_token_source.IsCancellationRequested) return; Thanks for your solution... – davidOhara Sep 25 '15 at 11:22
  • You welcome. I am curious, where did you put `if (cancellation_token_source.IsCancellationRequested) return;`? – Yacoub Massad Sep 25 '15 at 11:23
  • Here var stream_wrapper = new ReadNotifierStreamWrapper(ms , bytes => {if (cancellation_token_source.IsCancellationRequested) return; ...} – davidOhara Sep 25 '15 at 11:50
  • Strange! this should have no/minor effect. Anyway, I am happy you have solved your issue. – Yacoub Massad Sep 25 '15 at 12:02
0

The exception you get;

EventSourceException: No Free Buffers available from the operating system

is caused by the Debug.WriteLine, which uses ETW (Event Tracing for Windows). ETW is a Windows kernel-level tracing facility that relies on a number of buffers to cache data before writing it to disk. ETW uses the buffer size and the size of physical memory to calculate the maximum number of buffers allocated for the event tracing session's buffer pool. So, if your application is 64-bit ETW will allocate more buffers due to there being more addressable memory available.

The reason you are hitting the limit is that you are writing debugging events faster than ETW can process the buffers.

I believe it is possible to increase the maximum number of buffers using registry entries, but this will increase memory consumption and you have the potential to hit the limit again with bigger files, slower connection, etc. So your best bet is to either sleep the thread on each loop to write tracing events at a lower rate or replace the Debug.WriteLine with some other form of logging.

Glen Thomas
  • 10,190
  • 5
  • 33
  • 65