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

Creating animated gif image from non FX App thread causes exception

XMLWordPrintable

    • x86_64
    • windows_10

        ADDITIONAL SYSTEM INFORMATION :
        I have replicated this on Java 8u191 and Java 11.0.2 with JavaFX 11.0.2

        A DESCRIPTION OF THE PROBLEM :
        Creating animated javafx.scene.image.Image from 2 threads causes ArrayIndexOutOfBoundsException exception.
        Issue is caused by the fact that animated Images adds pulse listener without checking if it's invoked from fx thread, so there might be a case where two separate threads breaks pulse listener array in "AbstractMasterTimer" called "receivers".

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        1. Provide all dependencies to attached code (javafx-controls) and "cat.gif" as a resource, which might be any gif animated image with more than 1 frame.
        2. Run the code.
        3. Exception should be shown in console window
        Exception might not occur on every application start, but on 8/10 occurs in a different way.

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        Toolkit#checkFxUserThread() is invoked while retrieving AbstractMasterTimer from Toolkit#getMasterTimer() and if Image is created on separate thread, an exception is thrown
        ACTUAL -
        ArrayIndexOutOfBoundsException exception is thrown

        ---------- BEGIN SOURCE ----------
        package com.example;

        import java.io.IOException;
        import java.io.InputStream;
        import java.util.concurrent.CountDownLatch;

        import com.sun.javafx.tk.Toolkit;
        import javafx.application.Application;
        import javafx.scene.Scene;
        import javafx.scene.image.Image;
        import javafx.scene.image.ImageView;
        import javafx.scene.layout.BorderPane;
        import javafx.stage.Stage;

        public class SimpleMain
        {

            public static void main( String[] args ) throws Exception
            {
                final int threadNo = 4;
                final CountDownLatch latch = new CountDownLatch( threadNo + 1 );
                for( int i = 0; i < threadNo; i++ )
                {
                    new Thread( () -> {
                        latch.countDown();
                        try
                        {
                            latch.await();
                        }
                        catch( InterruptedException aE )
                        {
                            aE.printStackTrace();
                        }
                        try
                        {
                            createImage();
                        }
                        catch( Exception aE )
                        {
                            aE.printStackTrace();
                        }
                    } ).start();
                }
                Toolkit.getToolkit().getMasterTimer().addPulseReceiver( now -> {
                    latch.countDown();
                } );
                Application.launch( MainFx.class, args );
                latch.await();
            }

            private static Image createImage()
            {
                try (final InputStream catResource = SimpleMain.class.getResourceAsStream( "/cat.gif" ))
                {
                    return new Image( catResource );
                }
                catch( IOException aE )
                {
                    throw new IllegalStateException( aE );
                }
            }

            public static class MainFx extends Application
            {

                @Override
                public void start( final Stage primaryStage ) throws Exception
                {
                    System.err.println( Runtime.version().toString() );
                    System.err.println( System.getProperty( "javafx.version" ) );
                    System.err.println( System.getProperty( "javafx.runtime.version" ) );
                    final int threadNo = 4;
                    final CountDownLatch latch = new CountDownLatch( threadNo + 1 );
                    for( int i = 0; i < threadNo; i++ )
                    {
                        new Thread( () -> {
                            latch.countDown();
                            try
                            {
                                latch.await();
                            }
                            catch( InterruptedException aE )
                            {
                                aE.printStackTrace();
                            }
                            try
                            {
                                createImage();
                            }
                            catch( Exception aE )
                            {
                                aE.printStackTrace();
                            }
                        } ).start();
                    }
                    latch.countDown();
                    latch.await();
                    final Image catImage = createImage();
                    final Scene aScene = new Scene( new BorderPane( new ImageView( catImage ) ), 800, 600 );
                    primaryStage.setScene( aScene );
                    primaryStage.show();
                }

            }
        }

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

        CUSTOMER SUBMITTED WORKAROUND :
        Do not create animated images on non-FX thread

        FREQUENCY : often


              arapte Ambarish Rapte
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

                Created:
                Updated:
                Resolved: