2014-12-05 11:20:51 +00:00
/ *
2022-10-20 10:33:18 +00:00
* Copyright ( c ) 2013 , 2022 , Oracle and / or its affiliates . All rights reserved .
2014-12-05 11:20:51 +00:00
* 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 .
*
* 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 .
* /
import java.lang.management.ManagementFactory ;
import java.lang.management.ThreadInfo ;
import java.security.CodeSource ;
import java.security.Permission ;
import java.security.PermissionCollection ;
import java.security.Permissions ;
import java.security.Policy ;
import java.security.ProtectionDomain ;
import java.util.Enumeration ;
import java.util.concurrent.Semaphore ;
import java.util.concurrent.atomic.AtomicBoolean ;
import java.util.concurrent.atomic.AtomicInteger ;
import java.util.logging.LogManager ;
import java.util.logging.Logger ;
2018-11-06 18:01:16 +00:00
import jdk.internal.access.JavaAWTAccess ;
import jdk.internal.access.SharedSecrets ;
2014-12-05 11:20:51 +00:00
/ * *
* @test
* @bug 8065991
* @summary check that when LogManager is initialized , a deadlock similar
* to that described in 8065709 will not occur .
2018-11-06 18:01:16 +00:00
* @modules java . base / jdk . internal . access
2016-02-09 02:14:48 +00:00
* java . logging
* java . management
2014-12-05 11:20:51 +00:00
* @run main / othervm LogManagerAppContextDeadlock UNSECURE
* @run main / othervm LogManagerAppContextDeadlock SECURE
*
* @author danielfuchs
* /
public class LogManagerAppContextDeadlock {
public static final Semaphore sem = new Semaphore ( 0 ) ;
public static final Semaphore sem2 = new Semaphore ( 0 ) ;
public static final Semaphore sem3 = new Semaphore ( - 2 ) ;
public static volatile boolean goOn = true ;
public static volatile Exception thrown ;
// Emulate EventQueue
static class FakeEventQueue {
static final Logger logger = Logger . getLogger ( " foo " ) ;
}
// Emulate AppContext
static class FakeAppContext {
2015-09-16 04:56:04 +00:00
static final AtomicInteger numAppContexts = new AtomicInteger ( 0 ) ;
2014-12-05 11:20:51 +00:00
static final class FakeAppContextLock { }
static final FakeAppContextLock lock = new FakeAppContextLock ( ) ;
static volatile FakeAppContext appContext ;
final FakeEventQueue queue ;
FakeAppContext ( ) {
appContext = this ;
numAppContexts . incrementAndGet ( ) ;
// release sem2 to let Thread t2 call Logger.getLogger().
sem2 . release ( ) ;
try {
// Wait until we JavaAWTAccess is called by LogManager.
// Thread 2 will call Logger.getLogger() which will
// trigger a call to JavaAWTAccess - which will release
// sem, thus ensuring that Thread #2 is where we want it.
sem . acquire ( ) ;
System . out . println ( " Sem acquired: Thread #2 has called JavaAWTAccess " ) ;
} catch ( InterruptedException x ) {
Thread . interrupted ( ) ;
}
queue = new FakeEventQueue ( ) ;
}
static FakeAppContext getAppContext ( ) {
synchronized ( lock ) {
if ( numAppContexts . get ( ) = = 0 ) {
return new FakeAppContext ( ) ;
}
return appContext ;
}
}
static {
2015-09-28 12:39:27 +00:00
SharedSecrets . setJavaAWTAccess ( new JavaAWTAccess ( ) {
2014-12-05 11:20:51 +00:00
@Override
public Object getAppletContext ( ) {
if ( numAppContexts . get ( ) = = 0 ) return null ;
// We are in JavaAWTAccess, we can release sem and let
// FakeAppContext constructor proceeed.
System . out . println ( " Releasing Sem " ) ;
sem . release ( ) ;
return getAppContext ( ) ;
}
} ) ;
}
}
// Test with or without a security manager
public static enum TestCase {
UNSECURE , SECURE ;
public void run ( ) throws Exception {
System . out . println ( " Running test case: " + name ( ) ) ;
Configure . setUp ( this ) ;
test ( this ) ;
}
}
public static void test ( TestCase test ) throws Exception {
Thread t1 = new Thread ( ) {
@Override
public void run ( ) {
sem3 . release ( ) ;
System . out . println ( " FakeAppContext.getAppContext() " ) ;
FakeAppContext . getAppContext ( ) ;
System . out . println ( " Done: FakeAppContext.getAppContext() " ) ;
}
} ;
t1 . setDaemon ( true ) ;
t1 . start ( ) ;
Thread t2 = new Thread ( ) {
2016-02-26 11:11:28 +00:00
public Object logger ;
2014-12-05 11:20:51 +00:00
public void run ( ) {
sem3 . release ( ) ;
try {
// Wait until Thread1 is in FakeAppContext constructor
sem2 . acquire ( ) ;
System . out . println ( " Sem2 acquired: Thread #1 will be waiting to acquire Sem " ) ;
} catch ( InterruptedException ie ) {
Thread . interrupted ( ) ;
}
System . out . println ( " Logger.getLogger(name).info(name) " ) ;
2016-02-26 11:11:28 +00:00
// stick the logger in an instance variable to prevent it
// from being garbage collected before the main thread
// calls LogManager.getLogger() below.
logger = Logger . getLogger ( test . name ( ) ) ; //.info(name);
2014-12-05 11:20:51 +00:00
System . out . println ( " Done: Logger.getLogger(name).info(name) " ) ;
}
} ;
t2 . setDaemon ( true ) ;
t2 . start ( ) ;
System . out . println ( " Should exit now... " ) ;
Thread detector = new DeadlockDetector ( ) ;
detector . start ( ) ;
// Wait for the 3 threads to start
sem3 . acquire ( ) ;
// Now wait for t1 & t2 to finish, or for a deadlock to be detected.
while ( goOn & & ( t1 . isAlive ( ) | | t2 . isAlive ( ) ) ) {
if ( t2 . isAlive ( ) ) t2 . join ( 1000 ) ;
if ( test = = TestCase . UNSECURE & & System . getSecurityManager ( ) = = null ) {
// if there's no security manager, AppContext.getAppContext() is
// not called - so Thread t2 will not end up calling
// sem.release(). In that case we must release the semaphore here
// so that t1 can proceed.
if ( LogManager . getLogManager ( ) . getLogger ( TestCase . UNSECURE . name ( ) ) ! = null ) {
// means Thread t2 has created the logger
sem . release ( ) ;
}
}
if ( t1 . isAlive ( ) ) t1 . join ( 1000 ) ;
}
if ( thrown ! = null ) {
throw thrown ;
}
}
// Thrown by the deadlock detector
static final class DeadlockException extends RuntimeException {
public DeadlockException ( String message ) {
super ( message ) ;
}
@Override
public void printStackTrace ( ) {
}
}
public static void main ( String [ ] args ) throws Exception {
if ( args . length = = 0 ) {
args = new String [ ] { " SECURE " } ;
}
// If we don't initialize LogManager here, there will be
// a deadlock.
2022-10-20 10:33:18 +00:00
// See <https://bugs.openjdk.org/browse/JDK-8065709?focusedCommentId=13582038&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13582038>
2014-12-05 11:20:51 +00:00
// for more details.
Logger . getLogger ( " main " ) . info ( " starting... " ) ;
try {
TestCase . valueOf ( args [ 0 ] ) . run ( ) ;
System . out . println ( " Test " + args [ 0 ] + " Passed " ) ;
} catch ( Throwable t ) {
System . err . println ( " Test " + args [ 0 ] + " failed: " + t ) ;
t . printStackTrace ( ) ;
}
}
// Called by the deadlock detector when a deadlock is found.
static void fail ( Exception x ) {
x . printStackTrace ( ) ;
if ( thrown = = null ) {
thrown = x ;
}
goOn = false ;
}
// A thread that detect deadlocks.
2015-09-16 04:56:04 +00:00
static final class DeadlockDetector extends Thread {
2014-12-05 11:20:51 +00:00
public DeadlockDetector ( ) {
this . setDaemon ( true ) ;
}
@Override
public void run ( ) {
sem3 . release ( ) ;
Configure . doPrivileged ( this : : loop ) ;
}
public void loop ( ) {
while ( goOn ) {
try {
long [ ] ids = ManagementFactory . getThreadMXBean ( ) . findDeadlockedThreads ( ) ;
ids = ids = = null ? new long [ 0 ] : ids ;
if ( ids . length = = 1 ) {
throw new RuntimeException ( " Found 1 deadlocked thread: " + ids [ 0 ] ) ;
} else if ( ids . length > 0 ) {
ThreadInfo [ ] infos = ManagementFactory . getThreadMXBean ( ) . getThreadInfo ( ids , Integer . MAX_VALUE ) ;
System . err . println ( " Found " + ids . length + " deadlocked threads: " ) ;
for ( ThreadInfo inf : infos ) {
System . err . println ( inf ) ;
}
throw new DeadlockException ( " Found " + ids . length + " deadlocked threads " ) ;
}
Thread . sleep ( 100 ) ;
} catch ( InterruptedException | RuntimeException x ) {
fail ( x ) ;
}
}
}
}
// A helper class to configure the security manager for the test,
// and bypass it when needed.
static class Configure {
static Policy policy = null ;
static final ThreadLocal < AtomicBoolean > allowAll = new ThreadLocal < AtomicBoolean > ( ) {
@Override
protected AtomicBoolean initialValue ( ) {
return new AtomicBoolean ( false ) ;
}
} ;
static void setUp ( TestCase test ) {
switch ( test ) {
case SECURE :
if ( policy = = null & & System . getSecurityManager ( ) ! = null ) {
throw new IllegalStateException ( " SecurityManager already set " ) ;
} else if ( policy = = null ) {
policy = new SimplePolicy ( TestCase . SECURE , allowAll ) ;
Policy . setPolicy ( policy ) ;
System . setSecurityManager ( new SecurityManager ( ) ) ;
}
if ( System . getSecurityManager ( ) = = null ) {
throw new IllegalStateException ( " No SecurityManager. " ) ;
}
if ( policy = = null ) {
throw new IllegalStateException ( " policy not configured " ) ;
}
break ;
case UNSECURE :
if ( System . getSecurityManager ( ) ! = null ) {
throw new IllegalStateException ( " SecurityManager already set " ) ;
}
break ;
default :
new InternalError ( " No such testcase: " + test ) ;
}
}
static void doPrivileged ( Runnable run ) {
allowAll . get ( ) . set ( true ) ;
try {
run . run ( ) ;
} finally {
allowAll . get ( ) . set ( false ) ;
}
}
}
// A Helper class to build a set of permissions.
2015-09-16 04:56:04 +00:00
static final class PermissionsBuilder {
2014-12-05 11:20:51 +00:00
final Permissions perms ;
public PermissionsBuilder ( ) {
this ( new Permissions ( ) ) ;
}
public PermissionsBuilder ( Permissions perms ) {
this . perms = perms ;
}
public PermissionsBuilder add ( Permission p ) {
perms . add ( p ) ;
return this ;
}
public PermissionsBuilder addAll ( PermissionCollection col ) {
if ( col ! = null ) {
for ( Enumeration < Permission > e = col . elements ( ) ; e . hasMoreElements ( ) ; ) {
perms . add ( e . nextElement ( ) ) ;
}
}
return this ;
}
public Permissions toPermissions ( ) {
final PermissionsBuilder builder = new PermissionsBuilder ( ) ;
builder . addAll ( perms ) ;
return builder . perms ;
}
}
// Policy for the test...
public static class SimplePolicy extends Policy {
2019-06-21 20:04:14 +00:00
static final Policy DEFAULT_POLICY = Policy . getPolicy ( ) ;
2014-12-05 11:20:51 +00:00
final Permissions permissions ;
final Permissions allPermissions ;
final ThreadLocal < AtomicBoolean > allowAll ; // actually: this should be in a thread locale
public SimplePolicy ( TestCase test , ThreadLocal < AtomicBoolean > allowAll ) {
this . allowAll = allowAll ;
// we don't actually need any permission to create our
// FileHandlers because we're passing invalid parameters
// which will make the creation fail...
permissions = new Permissions ( ) ;
2018-11-06 18:01:16 +00:00
permissions . add ( new RuntimePermission ( " accessClassInPackage.jdk.internal.access " ) ) ;
2014-12-05 11:20:51 +00:00
// these are used for configuring the test itself...
allPermissions = new Permissions ( ) ;
allPermissions . add ( new java . security . AllPermission ( ) ) ;
}
@Override
public boolean implies ( ProtectionDomain domain , Permission permission ) {
if ( allowAll . get ( ) . get ( ) ) return allPermissions . implies ( permission ) ;
2019-06-21 20:04:14 +00:00
return permissions . implies ( permission ) | | DEFAULT_POLICY . implies ( domain , permission ) ;
2014-12-05 11:20:51 +00:00
}
@Override
public PermissionCollection getPermissions ( CodeSource codesource ) {
return new PermissionsBuilder ( ) . addAll ( allowAll . get ( ) . get ( )
? allPermissions : permissions ) . toPermissions ( ) ;
}
@Override
public PermissionCollection getPermissions ( ProtectionDomain domain ) {
return new PermissionsBuilder ( ) . addAll ( allowAll . get ( ) . get ( )
? allPermissions : permissions ) . toPermissions ( ) ;
}
}
}