Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8231558: [macos] Platform.exit causes assertion error on macOS 10.15 or later #42

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -42,7 +42,7 @@ - (id)initWithEnv:(JNIEnv*)env accessible:(jobject)acc

- (void)dealloc
{
GET_MAIN_JENV;
GET_MAIN_JENV_NOWARN;
if (env != NULL) {
(*env)->DeleteGlobalRef(env, jAccessible);
GLASS_CHECK_EXCEPTION(env);
@@ -218,11 +218,19 @@ do {
}


// retrieve main thread Java env asserting the call originated on main thread
// Retrieve Java env, asserting the call originated on main thread.
// Warn if the JVM has already been detached.
#define GET_MAIN_JENV \
assert(pthread_main_np() == 1); \
if (jEnv == NULL) \
GLASS_CALLSTACK("Java has been detached already, but someone is still trying to use it at ") \
JNIEnv *env = jEnv;

// Retrieve Java env, asserting the call originated on main thread.
// This variant is silent if the JVM has been detached, making it suitable
// for use by dealloc methods, which are called by the auto-release mechanism.
#define GET_MAIN_JENV_NOWARN \
assert(pthread_main_np() == 1); \
JNIEnv *env = jEnv;

#endif
@@ -137,7 +137,7 @@ - (id)initWithJavajdelegate:(jobject)jdelegate jtitle:(jstring)jtitle

- (void)dealloc
{
GET_MAIN_JENV;
GET_MAIN_JENV_NOWARN;

if (env != NULL)
{
@@ -201,7 +201,8 @@ - (void)dealloc

[GlassTouches stopTracking:self];

GET_MAIN_JENV;
GET_MAIN_JENV_NOWARN;

if (env != NULL)
{
(*env)->DeleteGlobalRef(env, self->jView);
@@ -66,4 +66,6 @@ public class Constants {
static final int ERROR_STARTUP_FAILED = 27;

static final int ERROR_ASSERTION_FAILURE = 28;

static final int ERROR_TIMEOUT = 29;
}
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package test.launchertest;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.layout.StackPane;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;

import static test.launchertest.Constants.*;

/**
* Test application that calls Platform.exit while Stage is still showing
* the Scene.
*
* This is launched by PlatformExitTest.
*/
public class PlatformExitApp extends Application {

// Timeout in milliseconds (must be at least 15 seconds)
private static final int TIMEOUT = 20000;

public static void sleep(long msec) {
try {
Thread.sleep(msec);
} catch (InterruptedException ex) {
ex.printStackTrace();
System.exit(ERROR_UNEXPECTED_EXCEPTION);
}
}

public static void setupTimeoutThread() {
// Timeout thread
Thread th = new Thread(() -> {
sleep(TIMEOUT);
System.exit(ERROR_TIMEOUT);
});
th.setDaemon(true);
th.start();
}

@Override public void start(Stage stage) throws Exception {
StackPane root = new StackPane();
Scene scene = new Scene(root, 400, 300);

final Label label = new Label("Hello");

root.getChildren().add(label);

stage.setScene(scene);
stage.show();

// Show window for 1 second before calling Platform.exit
Thread thr = new Thread(() -> {
sleep(1000);
Platform.exit();
});
thr.start();
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
setupTimeoutThread();
Application.launch(args);

// Short delay to allow any pending output to be flushed
sleep(500);
System.exit(ERROR_NONE);
}

}
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package test.launchertest;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import junit.framework.AssertionFailedError;
import org.junit.Test;
import test.util.Util;

import static test.launchertest.Constants.*;

/**
* Unit test for Platform.exit
*/
public class PlatformExitTest {

private static final String className = PlatformExitTest.class.getName();
private static final String pkgName = className.substring(0, className.lastIndexOf("."));
private static final String testAppName = pkgName + "." + "PlatformExitApp";

@Test (timeout = 15000)
public void testPlatformExit() throws Exception {

final ArrayList<String> cmd =
Util.createApplicationLaunchCommand(testAppName, null, null);

ProcessBuilder builder = new ProcessBuilder(cmd);
builder.redirectErrorStream(true);
Process process = builder.start();
final InputStream in = process.getInputStream();

// Wait for the process to exit
int retVal = process.waitFor();
switch (retVal) {
case 0:// SUCCESS
case ERROR_NONE:
break;

case 1:
throw new AssertionFailedError(testAppName
+ ": unable to launch java application");

case ERROR_TIMEOUT:
throw new AssertionFailedError(testAppName
+ ": application timeout");

case ERROR_UNEXPECTED_EXCEPTION:
throw new AssertionFailedError(testAppName
+ ": unexpected exception");

default:
throw new AssertionFailedError(testAppName
+ ": Unexpected error exit: " + retVal);
}

// Read the output of the forked process and check for warning string
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder = stringBuilder.append(line).append("\n");
}
if (stringBuilder.indexOf("Java has been detached") >= 0) {
System.err.println(stringBuilder);
throw new AssertionFailedError(testAppName + ": tried to use JNI after Java was detached");
}
}

}