views:

426

answers:

3

Now I appreciate the moral of the story is "don't hog the UI thread" but we tried to KISS by keeping things on the UI thread for as long as possible but I think we've just hit the tipping point and we're going to have to change our design.

But anyway..... something I noticed on our device that doesn't happen on desktop: When you hog the UI thread it drops keys. Here is a very simple app that displays the problem.

using System;
using System.Windows.Forms;

namespace DeviceApplication14
{
    public partial class Form1 : Form
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [MTAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }

        private int ctr;

        public Form1()
        {
            InitializeComponent();
            KeyPreview = true;
            KeyDown += Form1_KeyDown;
        }

        void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            for (int i = 0; i < 1000000; i++)
            {

            }
            ctr++;
            button1.Text = ctr.ToString();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            ctr = 0;
            button1.Text = ctr.ToString();
        }

        /// <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.button2 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(235, 137);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(329, 239);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            // 
            // button2
            // 
            this.button2.Location = new System.Drawing.Point(62, 291);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(72, 20);
            this.button2.TabIndex = 1;
            this.button2.Text = "Clear";
            this.button2.Click += new System.EventHandler(this.button2_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
            this.AutoScroll = true;
            this.ClientSize = new System.Drawing.Size(638, 455);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }

        #endregion

        private Button button1;
        private Button button2;
    }
}

When I run this on my custom box I can press four keys in succession but my ctr only increments by two. If I then add a few zeros (to compensate for the desktop's greater speed) and run it on desktop I get all the keys.

What's going on here?

+1  A: 

Windows CE won't "drop" any keys once the message gets into the queue. My guess is that you're using so much quantum that the keyboard driver itself isn't getting all of the keys, and therefore can't pass them on. You could verify this by calling PostKeybdMessage instead of using the keyboard itself to rule out hardware or driver interaction.

ctacke
Awesome! Thanks i'll see if I can verify this. So the basic premise is that i'm burning the CPU so much that the driver doesn't get the time to process them?
Quibblesome
Yes, that's my bet.
ctacke
Well with the same code and a timer kicking off 10 immediate calls to this method I get all 10. Which suggests there is probably some time sensitive code further towards the keyboard. Riddle more or less answered. Thank you! :D
Quibblesome
or a very small keyboard buffer that gets overwritten.
Quibblesome
+2  A: 
for (int i = 0; i < 1000000; i++)
            {

            }

Empty loops to provide time delays are indicative of poor understanding of the EVENT driven model.

Windows CE has a different enough event handler from the desktop version of windows that, while windows tolerates this type of abuse, windows CE will not.

If you soak up a lot of processing time in empty loops such as this rather than calling a delay routine which properly releases control back to the scheduler then the event handler isn't going to be called. If the event handler isn't called frequently enough then you're going to lose events. There are various buffers that store some of these events, but depending on the input devices (keyboard, touchscreen, etc) those buffers may be inadequate or nonexistant.

The kicker here is that the event handler on a portable device doesn't have a big buffer. The desktop computer has a ton of buffers for both keyboard and mouse actions.

There are many other differences, but the bottom line is that you really, really need to release the thread properly and delay properly rather than these simple empty loop delays.

A windows CE timer would be more appropriate here - kick off the timer with the keydown routine, and when it triggers do the action needed. This will release control back to the UI so that other events can run.

Also, keep in mind that while windows CE is multithreaded/multitasking, it's best to think of your program as only running one thing at a time. The OS and event model may be sending your app events (and not buffering them) and your app is sitting in this delay routine ignoring them. You won't see other events (including keydowns) while that loop is executing. Depending on the load and performance of the system that delay might run for a few milliseconds to hundreds of milliseconds - you can't count on it to run the same length of time each time it runs - another reason to use real timers.

Adam Davis
I'm fairly certain his spin loop is only used to demonstrate the issue as he sees it - he can't actually be doing that in an app. I hope anyway.
ctacke
ROFL..... man if I used that in production code..... It is just to simulate hogging the UI thread in this instance as I wanted to test this in an environment removed from the main app i'm working on.
Quibblesome
+1  A: 

The issue was to do with a bug in the BSP (apparently from some Freescale code) that meant that the keyboard driver was functioning at a much lower priority interrupt than was intended.

This is now fixed and everything works awesome fine. :)

Quibblesome
+1 for coming back and closing the loop.
ctacke
True moral of the story: Never assume the platform is bug-free.
ctacke