Skip to content

On Mono-Linux, there is unexpected behavior with resizing forms as compared to .NET runtime and Mono runtime on Windows machines #21836

@laustin254

Description

@laustin254

I have a program that runs a thread which does BeginInvoke to update a label continuously with 20 ms sleeps. There's a button that resizes the form continuously (this.Size = new Size(..)) in a while loop with conditional that it only runs for 5 seconds. On .NET runtime on Windows and Mono runtime on Windows I'm able to run this same code and the application successfully completes the while-loop and becomes responsive to user input after 5 seconds (ish), which is not happening on Mono runtime on Linux. On Mono-Linux, it seems like there's unexpected behavior with resizing as compared to .NET runtime and Mono runtime on Windows machines. I'm finding this to be problematic most specifically when I have continuous background thread operations that use BeginInvoke to update the GUI.

Link to wine mono gitlab issue (duplicate) wine-mono#1. Let me know which issue I should delete, this one or that one.

Steps to Reproduce

  1. Create a new project and add this code to a .NET framework 4.5.2 project
  2. Run the program on a Linux machine

Code

using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;

namespace FormsApp2
{

internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
    public class Form1 : Form
    {
        CancellationTokenSource cancellationTokenSource;
        private bool countActive = false;
        System.Threading.Thread T;
        private Color color1 = Color.Plum;
        private Color color2 = Color.RoyalBlue;
        Size minSize;
        Size maxSize;

        public Form1()
        {
            InitializeComponent();
            this.DoubleBuffered = true;
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer |
                          ControlStyles.UserPaint |
                          ControlStyles.AllPaintingInWmPaint, true);
            this.UpdateStyles();

            button1.Click += Button1_Click;
            button2.Click += Button2_Click;
            Resize += Form1_Resize;
            FormClosed += Form1_FormClosed;

            minSize = new Size(this.Size.Width, this.Size.Height);
            maxSize = new Size(this.Size.Width + 200, this.Size.Height + 200);
        }

        private void Button2_Click(object sender, EventArgs e)
        {
            Console.WriteLine(Environment.StackTrace);
            try
            {
                var start = DateTime.UtcNow;
                while ((DateTime.UtcNow - start).TotalMilliseconds < 5000)
                {
                    Console.WriteLine("Resizing");
                    if ((DateTime.UtcNow - start).TotalMilliseconds < 2500 && this.Size.Width <= maxSize.Width)
                    {
                        this.Size = new Size(this.Width + 1, this.Height + 1);
                    }
                    else if (this.Size.Width >= minSize.Width)
                    {
                        this.Size = new Size(this.Width - 1, this.Height - 1);
                    }
                }
            }
            catch(Exception ex)
            {                
                closeEverything();
            }
            finally
            {
                Console.WriteLine(Environment.StackTrace);
                Console.WriteLine("Button down press handler completed.");
            }
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            Console.WriteLine("Entered Form1_Resize");
            
            UpdateColorOfLabel();
        }

        private void Button1_Click(object sender, EventArgs e)
        {
            countActive = !countActive;
            if (countActive)
            {
                cancellationTokenSource = new CancellationTokenSource();
                T = new System.Threading.Thread(() => { Increment(cancellationTokenSource.Token); });
                T.Start();
            }
            else
            {
                closeEverything();
            }
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            closeEverything();
        }
        
        private void closeEverything()
        {
            if (T != null && cancellationTokenSource != null)
            {
                cancellationTokenSource.Cancel();
                T.Join();
                cancellationTokenSource.Dispose();
                cancellationTokenSource = null;
            }
        }

        private void UpdateColorOfLabel()
        {
            if (label2.BackColor == color1)
                label2.BackColor = color2;
            else
                label2.BackColor = color1;

        }

        private void UpdateLabel(string Text)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new Action(() => { UpdateLabel(Text); }));
                return;
            }

            Console.WriteLine("Label is: " + Text);

            label1.Text = Text;
        }

        private void Increment(CancellationToken token)
        {
            long i = 0;
            try
            {
                while (true && countActive && !token.IsCancellationRequested)
                {
                    Thread.Sleep(20);
                    i = (i + 1) % (long.MaxValue - 1);
                    UpdateLabel(i.ToString());
                }
            }
            catch (Exception Ex)
            {
            }
        }

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.button2 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(40, 45);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(145, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "Start/Stop Cntr";
            this.button1.UseVisualStyleBackColor = true;
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(290, 54);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(35, 13);
            this.label1.TabIndex = 1;
            this.label1.Text = "label1";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.BackColor = System.Drawing.Color.RoyalBlue;
            this.label2.Location = new System.Drawing.Point(452, 54);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(19, 13);
            this.label2.TabIndex = 2;
            this.label2.Text = "    ";
            // 
            // button2
            // 
            this.button2.Location = new System.Drawing.Point(40, 74);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(145, 23);
            this.button2.TabIndex = 3;
            this.button2.Text = "Resize";
            this.button2.UseVisualStyleBackColor = true;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(538, 133);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.Button button2;
    }
}

Current Behavior

Program becomes unresponsive after 5 seconds (Mono - Linux)

Expected Behavior

While loop should end after 5 seconds and program to become responsive again.

On which platforms did you notice this

[ ] macOS
[X] Linux
[ ] Windows

Version Used:

Mono JIT compiler version 6.12.0.200 (tarball Tue Jul 11 21:37:50 UTC 2023)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
TLS: __thread
SIGSEGV: altstack
Notifications: epoll
Architecture: amd64
Disabled: none
Misc: softdebug
Interpreter: yes
LLVM: yes(610)
Suspend: hybrid
GC: sgen (concurrent by default)

Stacktrace

Please paste the stack trace here if available.

This is the stack at the time of entering the while loop:
at System.Environment.get_StackTrace () [0x00000] in :0
at FormsApp2.Form1.Button2_Click (System.Object sender, System.EventArgs e) [0x00000] in <6ef6bc9fae37483ca2d55111084fbc1c>:0
at System.Windows.Forms.Control.OnClick (System.EventArgs e) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.Button.OnClick (System.EventArgs e) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.ButtonBase.OnMouseUp (System.Windows.Forms.MouseEventArgs mevent) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.Button.OnMouseUp (System.Windows.Forms.MouseEventArgs mevent) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.Control.WmLButtonUp (System.Windows.Forms.Message& m) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.Control.WndProc (System.Windows.Forms.Message& m) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.ButtonBase.WndProc (System.Windows.Forms.Message& m) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.Button.WndProc (System.Windows.Forms.Message& m) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.Control+ControlWindowTarget.OnMessage (System.Windows.Forms.Message& m) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.Control+ControlNativeWindow.WndProc (System.Windows.Forms.Message& m) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.NativeWindow.WndProc (System.IntPtr hWnd, System.Windows.Forms.Msg msg, System.IntPtr wParam, System.IntPtr lParam) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.XplatUIX11.DispatchMessage (System.Windows.Forms.MSG& msg) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.XplatUI.DispatchMessage (System.Windows.Forms.MSG& msg) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.Application.RunLoop (System.Boolean Modal, System.Windows.Forms.ApplicationContext context) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.Application.Run (System.Windows.Forms.ApplicationContext context) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at System.Windows.Forms.Application.Run (System.Windows.Forms.Form mainForm) [0x00000] in <25c2bdf9d79a43a599eb46bd15cd77fb>:0
at FormsApp2.Program.Main () [0x00000] in <6ef6bc9fae37483ca2d55111084fbc1c>:0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions