Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-1233261

AWT-Input thread pushes CPU to 100% while waiting with focus on TextField

XMLWordPrintable

    • 1.0.2
    • sparc
    • solaris_2.4, solaris_2.5
    • Verified


      An application or applet can peg the CPU at 100% (seen on Performance
      Tool) while idle if the focus is on a TextField. This was noticed on
      the Converter application from the Java Language Tutorial

          http://www.javasoft.com/tutorial/ui/overview/example/Converter.java

      This applet puts up two main Panels, each of which contains a
      TextField, Slider, and Choice. When the app first starts, CPU usage
      is low, but as soon as any Component is acted on, the top TextField
      is put in focus and the CPU use climbs to 100%.

      CPU use can be held lower than 50%, however, if the app receives a
      steady stream of events, such as clicking repeatedly on the Choice
      component or repeating keypresses in the TextField (e.g., holding the
      Backspace key down).

      Resizing the app window takes the TextField out of focus and the CPU
      use drops back to background levels.

      Running the app within jdb reveals that the AWT-Input thread is the
      culprit:

          1. (sun.awt.motif.InputThread)0xe630c718 AWT-Input running

      Suspending this thread drops the CPU use to very low levels; resuming
      it restores the above-described behavior.

      Source for the Converter applet is included below:

      ================================================================

      /*
       * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
       *
       * Permission to use, copy, modify, and distribute this software
       * and its documentation for NON-COMMERCIAL purposes and without
       * fee is hereby granted provided that this copyright notice
       * appears in all copies. Please refer to the file "copyright.html"
       * for further important copyright and licensing information.
       *
       * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
       * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
       * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
       * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
       * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
       */
      /* This program could use some layout work, and the functionality
       * could use some tweaking, but it seems to basically work.
       */
      import java.awt.*;
      import java.util.*;
      import java.applet.Applet;

      public class Converter extends Applet {
          Frame window;
          ConversionPanel metricPanel, usaPanel;
          Unit metricDistances[] = new Unit[3];
          Unit usaDistances[] = new Unit[4];

          /** Create the ConversionPanels (one for metric, another for U.S.).
            * I used "U.S." because although Imperial and U.S. distance
            * measurements are the same, this program could be extended to
            * include volume measurements, which aren't the same.
            */
          public void init() {
      setLayout(new GridLayout(2,0,5,5));

      metricDistances[0] = new Unit("Centimeters", 0.01);
      metricDistances[1] = new Unit("Meters", 1.0);
      metricDistances[2] = new Unit("Kilometers", 1000.0);
      metricPanel = new ConversionPanel(this, "Metric System",
      metricDistances);

      usaDistances[0] = new Unit("Inches", 0.0254);
      usaDistances[1] = new Unit("Feet", 0.305);
      usaDistances[2] = new Unit("Yards", 0.914);
      usaDistances[3] = new Unit("Miles", 1613.0);
      usaPanel = new ConversionPanel(this, "U.S. System", usaDistances);

      add(metricPanel);
      add(usaPanel);
          }

          /** Does the conversion from metric to U.S., or vice versa, and
            * updates the appropriate ConversionPanel. */
          void convert(ConversionPanel from) {
      ConversionPanel to;

      if (from == metricPanel)
      to = usaPanel;
      else
      to = metricPanel;
      double multiplier = from.getMultiplier() / to.getMultiplier();
      to.setValue(from.getValue() * multiplier);
          }

          /** Draws a box around this panel. */
          public void paint(Graphics g) {
      Dimension d = size();
      g.drawRect(0,0, d.width - 1, d.height - 1);
          }

          /** Puts a little breathing space between
            * the panel and its contents, which lets us draw a box
            * in the paint() method.
            */
          public Insets insets() {
      return new Insets(5,5,5,5);
          }

          public static void main(String args[]) {
      Frame f = new Frame("Converter Applet/Application");
      Converter converter = new Converter();

      converter.init();

      f.add("Center", converter);
      f.pack();
      f.show();
          }

      }


      class ConversionPanel extends Panel {
          String title;
          TextField textField;
          Scrollbar slider;
          Choice unitChooser;
          int min = 0;
          int max = 10000;
          Converter controller;
          Unit units[];

          //TO DO: Should make both panels' choices the same width.
          ConversionPanel(Converter myController, String myTitle, Unit myUnits[]) {
      super();
      GridBagConstraints c = new GridBagConstraints();
      GridBagLayout gridbag = new GridBagLayout();
      setLayout(gridbag);
      controller = myController;
      title = myTitle;
      units = myUnits;

      //Set up default constraints
      c.fill = GridBagConstraints.HORIZONTAL;

      //Add the label
      Label label = new Label(title, Label.CENTER);
      c.weightx = 0.0;
      c.gridwidth = GridBagConstraints.REMAINDER;
      gridbag.setConstraints(label, c);
      add(label);

      //Add the text field
      textField = new TextField("0", 10);
      c.weightx = 1.0;
      c.gridwidth = GridBagConstraints.RELATIVE;
      gridbag.setConstraints(textField, c);
      add(textField);

      //Add the pop-up list (Choice)
      unitChooser = new Choice();
      for (int i = 0; i < units.length; i++) {
      unitChooser.addItem(units[i].description);
      }
      c.weightx = 0.0;
      c.gridwidth = GridBagConstraints.REMAINDER;
      gridbag.setConstraints(unitChooser, c);
      add(unitChooser);

      //Add the slider
      slider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 100, min, max);
      c.weightx = 0.0;
      c.gridheight = 1;
      c.gridwidth = GridBagConstraints.RELATIVE;
      gridbag.setConstraints(slider, c);
      add(slider);
          }

          /** Returns the multiplier (units/meter) for the currently
            * selected unit of measurement.
            */
          double getMultiplier() {
      int i = unitChooser.getSelectedIndex();
      return (units[i].multiplier);
          }

          /** Draws a box around this panel. */
          public void paint(Graphics g) {
      Dimension d = size();
      g.drawRect(0,0, d.width - 1, d.height - 1);
          }

          /** Puts a little breathing space between
            * the panel and its contents, which lets us draw a box
            * in the paint() method.
            * We add more pixels to the right, to work around a
            * Choice bug.
            */
          public Insets insets() {
      return new Insets(5,5,5,8);
          }

          /** Gets the current value in the text field.
            * That's guaranteed to be the same as the value
            * in the scroller (subject to rounding, of course).
            */
          double getValue() {
      double f;
      try {
      f = Double.valueOf(textField.getText()).doubleValue();
      } catch (java.lang.NumberFormatException e) {
      f = 0.0;
      }
      return f;
          }

          /** Respond to user actions. */
          public boolean handleEvent(Event e) {
      if (e.target instanceof Scrollbar) {
      textField.setText(String.valueOf(slider.getValue()));
      controller.convert(this);
      return true;
      } else if (e.target instanceof TextField) {
      setSliderValue(getValue());
      controller.convert(this);
      return true;
      } else if (e.target instanceof Choice) {
      controller.convert(this);
      return true;
      }
      return false;
          }

          /** Set the values in the slider and text field. */
          void setValue(double f) {
      setSliderValue(f);
              textField.setText(String.valueOf(f));
          }

          /** Set the slider value. */
          void setSliderValue(double f) {
      int sliderValue = (int)f;

      if (sliderValue > max)
          sliderValue = max;
      if (sliderValue < min)
      sliderValue = min;
              slider.setValue(sliderValue);
          }
      }


      class Unit {
          String description;
          double multiplier;

          Unit(String description, double multiplier) {
      super();
      this.description = description;
      this.multiplier = multiplier;
          }

          public String toString() {
      String s = "Meters/" + description + " = " + multiplier;
      return s;
          }
      }
      ================================================================

      The description field as copied from bug report 1238872 follows:

      If I have the mouse focused on a container (in this case, a Frame) which contains
      a TextField, the TextField seems to busywait for X input events, consuming 50%
      of CPU time in the process.

      An interesting symptom of this is that the cursor only blinks if the TextField's
      container notices an input event.

      A rather odd effect of this is that I can make my applications consume less CPU
      time by continuously wiggling the mouse.
      ________
      I'm adding this user's description of this problem because it includes a good code example and a very accurate description.
       
      Category: AWT

      Priority: CRITICAL! (Low/Medium/High/Critical)

      JDK version: JDK-1_0-solaris2-sparc.tar.Z

      Platforms: Solaris2.4, SPARCStation20


      Synopsis
      --------

      Motif textfield blocks repainting while it has the focus


      Description
      -----------

      This bug occurs on Motif.

      Click inside a textfield. Notice that the text cursor should be
      blinking, but is not. As you move the mouse, the text cursor will
      start to blink erratically.

      Repaints appear to be blocked while a textfield has the focus. When
      the mouse is moved, the queued up repaints will then be unblocked.
      When the mouse stops moving again, repainting once again becomes
      blocked.


      Test Case
      ---------

      - Compile and run textfield.java:

      javac textfield.java
      java textfield

      - The button has the focus. Notice the label updating every second.

      - Click on the textfield. Notice that the label does not update anymore,
        nor is the textfield cursor blinking.

      - Move the mouse about. The label starts to update again, and the textfield
        cursor begins to blink.

      - Stop moving the mouse. The label doesn't update, and the textfield cursor
        doesn't blink.

      - Click on the button. The textfield no longer has focus, so the label
        continues to update.


      Workaround
      ----------

      Calling setText on a label will cause pending repaints to be
      unblocked. The call to setText must contain a new value for the
      label, otherwise the label won't update.
      ----------
      X-Sun-Data-Type: default
      X-Sun-Data-Description: default
      X-Sun-Data-Name: textfield.java
      X-Sun-Charset: us-ascii
      X-Sun-Content-Lines: 66


      import java.awt.*;

      class CanvasLabel extends Canvas {
        private String text;

        public CanvasLabel (String text) {
          this.text = text;
        }

        public void setText(String text) {
          this.text = text;
          repaint();
        }

        public Dimension minimumSize() {
          int w, h;
          FontMetrics fm = getFontMetrics(getFont());
          w = fm.stringWidth(text);
          h = fm.getHeight();
          return new Dimension(w, h);
        }

        public Dimension preferredSize() {
          return minimumSize();
        }

        public void paint(Graphics g) {
          int x, y;

          Dimension d = size();
          FontMetrics fm = getFontMetrics(getFont());

          x = (d.width - fm.stringWidth(text))/2;
          y = d.height - 3;

          g.setFont(getFont());
          g.setColor(getForeground());
          g.drawString(text, x, y);
        }
      }

      public class textfield extends Frame {
        public static void main(String argv[]) {
          new textfield();
        }

        public textfield() {
          Button button = new Button("Button");
          TextField textfield = new TextField("TextField");
          CanvasLabel label = new CanvasLabel("0");

          add("North", button);
          add("Center", textfield);
          add("South", label);

          show();
          resize(preferredSize());
          validate();

          for (int i=0; ; i++) {
            label.setText(Integer.toString(i));
            try { Thread.sleep(1000); } catch (Exception ex) {}
          }
        }
      }

            amfowler Anne Fowler (Inactive)
            duke J. Duke
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: