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

Tooltips cannot be instantiated on background thread

XMLWordPrintable

    • Fix Understood
    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      Windows 10
      JRE 21

      A DESCRIPTION OF THE PROBLEM :
      Creating a tooltip with constructor new can lead to null pointer exceptions:
      - call `new Tooltip()`
      - tooltip does some initial CSS layouting
      - for this, some internal node tries to get it's stylable parent
      - Tooltip overwrites this with returning the global static variable `Tooltip.BEHAVIOUR.hoveredNode`, if it's not null

      If, at the same time, the user hovers or un-hovers a node, bookkeeping of the parent hierarchy in the background thread can get corrupted, leading to a NPE.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      - run the example
      - place your mouse at the border of the bottom of the label, so that you rapidly un-hover and hover the label
      - observe a NPE



      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      no NPE
      ACTUAL -
      an NPE

      ---------- BEGIN SOURCE ----------
      /*
       * This source file was generated by the Gradle 'init' task
       */
      package org.example.app

      import javafx.application.Application
      import javafx.scene.Scene
      import javafx.scene.control.Label
      import javafx.scene.control.Tooltip
      import javafx.scene.layout.Border
      import javafx.scene.layout.VBox
      import javafx.scene.paint.Color
      import javafx.stage.Stage
      import javafx.util.Duration
      import kotlinx.coroutines.CoroutineScope
      import kotlinx.coroutines.Dispatchers
      import kotlinx.coroutines.javafx.JavaFx
      import kotlinx.coroutines.launch
      import kotlinx.coroutines.yield
      import kotlin.coroutines.EmptyCoroutineContext

      class App: Application() {
        companion object {
          @JvmStatic
          fun main(args: Array<String>) {
            launch(App::class.java, *args)
          }
        }

        override fun start(stage: Stage) {

          val root = VBox()
          val scene = Scene(root)
          stage.scene = scene
          val text = Label()
          text.text = "some text"
          text.border = Border.stroke(Color.BLACK)
          val tooltip1 = Tooltip()
          tooltip1.showDelay = Duration(0.0)
          tooltip1.hideDelay = Duration(0.0)
          tooltip1.text = "tooltip1"
          text.tooltip = tooltip1
          root.children += text
          stage.show()
          val scope = CoroutineScope(EmptyCoroutineContext)
          // continuously resize text to force re-layout
          scope.launch(Dispatchers.JavaFx) {
            val h1 = 400.0
            val h2 = 410.0
            while (true) {
              yield()
              text.minHeight = h1
              text.maxHeight = h1
              stage.height = h1
              yield()
              text.minHeight = h2
              text.maxHeight = h2
              stage.height = h2
            }
          }
          scope.launch {
            while (true) {
              val tooltip2 = Tooltip()
            }
          }
        }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      create all tooltip instances on the JavaFX thread to ensure no concurrent update of the global static variable

      FREQUENCY : occasionally


      Root Cause:
      navigating up the parent hierarchy during CSS processing in a background thread encounters a null parent, causing the NPE.

        1. App.kt
          2 kB
          Anupam Dev
        2. Test.java
          2 kB
          Anupam Dev
        3. TooltipThreadingTest.java
          2 kB
          Kevin Rushforth

            angorya Andy Goryachev
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: