JDK-8130750 : JFXMedia Player EventQueueThread does not always terminate
  • Type: Bug
  • Component: javafx
  • Sub-Component: media
  • Affected Version: 8u45
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2015-06-26
  • Updated: 2020-01-31
  • Resolved: 2016-04-01
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u102Fixed 9Fixed
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) Client VM (build 25.45-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Windows 7 x64, Windows 8.1

A DESCRIPTION OF THE PROBLEM :
There is a race condition when disposing instances of com.sun.media.jfxmediaimpl.NativeMediaPlayer$EventQueueThread that prevents the thread from ever terminating.

The main culprit of the bug is in com.sun.media.jfxmediaimple.NativeMediaPlayer:1337-1341].  The event queue being processed by the leaking thread is cleared, however, this has the potential to remove the event that was added to the queue to notify the thread to stop, as a result it will hang forever on the take call.  It needs to ensure the event queue has finished executing before clearing the queue.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached application, with multiple video files for an extended period of time, since this is a race condition reproduction depends on the machines performance.

If you're impatient just set a single thread breakpoint on the EventQueueThread take call(NativeMediaPlayer:397), or add a sleep call immediately before it and the threads will leak almost every time.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
At most one JFXMedia Player EventQueueThread, since only one media object is ever playing at once in the sample application.
ACTUAL -
Hundreds of threads with a stack trace like:

 JFXMedia Player EventQueueThread tid=4720257 [WAITING] [DAEMON]
sun.misc.Unsafe.park(boolean, long)
java.util.concurrent.locks.LockSupport.park(Object)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await()
java.util.concurrent.LinkedBlockingQueue.take()
com.sun.media.jfxmediaimpl.NativeMediaPlayer$EventQueueThread.run()

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Eventually you encounter an out of memory error:
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - java.lang.OutOfMemoryError
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at sun.misc.Unsafe.allocateMemory(Native Method)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at java.nio.DirectByteBuffer.<init>(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at java.nio.ByteBuffer.allocateDirect(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at com.sun.prism.impl.BufferUtil.newByteBuffer(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at com.sun.prism.impl.BufferUtil.newIntBuffer(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at com.sun.prism.impl.QueuedPixelSource.getUnusedPixels(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at com.sun.javafx.tk.quantum.UploadingPainter.run(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at java.util.concurrent.FutureTask.runAndReset(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at com.sun.javafx.tk.RenderJob.run(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - 	at java.lang.Thread.run(Unknown Source)

REPRODUCIBILITY :
This bug can be reproduced often.

---------- BEGIN SOURCE ----------
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.io.File;
import java.util.List;

public class MediaThreadLeak extends Application
{
    private final List<File> files = FXCollections.observableArrayList( new File("C:/projects/t1.mp4"), new File("C:/projects/t2.mp4"));
    private final MediaView mv = new MediaView();

    private int mpi = -1;

    @Override
    public void start(Stage primaryStage) throws Exception
    {
        primaryStage.setTitle("Media Test");
        Group root = new Group();
        root.getChildren().add(mv);
        Scene scene = new Scene(root, 500, 500, Color.WHITE);
        primaryStage.setScene(scene);
        primaryStage.show();

        Timeline tl = new Timeline( new KeyFrame(Duration.millis(2000),e->changeMedia()));
        tl.setCycleCount(Timeline.INDEFINITE);
        tl.playFromStart();
    }

    private void changeMedia()
    {
        MediaPlayer mp = mv.getMediaPlayer();
        if( mp != null)
        {
            mv.setMediaPlayer(null);
            mp.stop();
            mp.dispose();
        }

        mpi = ++mpi >= files.size() ? 0 : mpi;
        mv.setMediaPlayer(new MediaPlayer(new Media(files.get(mpi).toURI().toString())));
        mv.getMediaPlayer().setAutoPlay(true);
        mv.setFitHeight(800);
        mv.setFitWidth(800);
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I see no workaround aside from modifying the javafx code or restarting the application periodically.


Comments
Approved for backport to 8u-dev (for 8u102).
09-04-2016

http://hg.openjdk.java.net/openjfx/9-dev/rt/rev/718367563b25
01-04-2016

Fix looks fine to me, approved
01-04-2016

Please review the following: http://cr.openjdk.java.net/~almatvee/8130750/webrev.00/ Reviewers: ddehaven EventQueueThread does not terminate sometimes due to race condition in dispose() method. In dispose() method we setting stopped to true, sending event to unblock queue and then we clear events in queue, so EventQueueThread might never receive this event. Fixed by not clearing event queue in dispose. EventQueueThread will clear events before exiting.
26-03-2016

Moving it to JDK for Dev teams evaluation.
08-07-2015