1 /** 2 * Logback: the reliable, generic, fast and flexible logging framework. 3 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 4 * 5 * This program and the accompanying materials are dual-licensed under 6 * either the terms of the Eclipse Public License v1.0 as published by 7 * the Eclipse Foundation 8 * 9 * or (per the licensee's choosing) 10 * 11 * under the terms of the GNU Lesser General Public License version 2.1 12 * as published by the Free Software Foundation. 13 */ 14 package ch.qos.logback.core.spi; 15 16 import ch.qos.logback.core.Appender; 17 import org.junit.jupiter.api.Disabled; 18 import org.junit.jupiter.api.Test; 19 import org.junit.jupiter.api.Timeout; 20 21 import java.util.concurrent.TimeUnit; 22 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.when; 25 26 /** 27 * This test shows the general problem I described in LOGBACK-102. 28 * 29 * In the two test cases below, an appender that throws an RuntimeException 30 * while getName is called - but this is just an example to show the general 31 * problem. 32 * 33 * The tests below fail without fixing LBCORE-67 and pass when Joern Huxhorn's 34 * patch is applied. 35 * 36 * Additionally, the following, probably more realistic, situations could 37 * happen: 38 * 39 * -addAppender: appenderList.add() could throw OutOfMemoryError. This could 40 * only be shown by using an appenderList mock but appenderList does not (and 41 * should not) have a setter. This would leave the write lock locked. 42 * 43 * -iteratorForAppenders: new ArrayList() could throw an OutOfMemoryError, 44 * leaving the read lock locked. 45 * 46 * I can't imagine a bad situation in isAttached, detachAppender(Appender) or 47 * detachAppender(String) but I'd change the code anyway for consistency. I'm 48 * also pretty sure that something stupid can happen at any time so it's best to 49 * just stick to conventions. 50 * 51 * @author Joern Huxhorn 52 */ 53 @Disabled 54 public class AppenderAttachableImplLockTest { 55 56 private AppenderAttachableImpl<Integer> aai = new AppenderAttachableImpl<Integer>(); 57 58 @SuppressWarnings("unchecked") 59 @Test 60 @Timeout(value=1, unit = TimeUnit.SECONDS) 61 public void getAppenderBoom() { 62 Appender<Integer> mockAppender1 = mock(Appender.class); 63 64 when(mockAppender1.getName()).thenThrow(new RuntimeException("oops")); 65 aai.addAppender(mockAppender1); 66 try { 67 // appender.getName called as a result of next statement 68 aai.getAppender("foo"); 69 } catch (RuntimeException e) { 70 // this leaves the read lock locked. 71 } 72 73 Appender<Integer> mockAppender2 = mock(Appender.class); 74 // the next call used to freeze with the earlier ReadWriteLock lock 75 // implementation 76 aai.addAppender(mockAppender2); 77 } 78 79 @SuppressWarnings("unchecked") 80 @Test 81 @Timeout(value=1, unit = TimeUnit.SECONDS) 82 public void detachAppenderBoom() throws InterruptedException { 83 Appender<Integer> mockAppender = mock(Appender.class); 84 when(mockAppender.getName()).thenThrow(new RuntimeException("oops")); 85 mockAppender.doAppend(17); 86 87 aai.addAppender(mockAppender); 88 Thread t = new Thread(new Runnable() { 89 90 public void run() { 91 try { 92 // appender.getName called as a result of next statement 93 aai.detachAppender("foo"); 94 } catch (RuntimeException e) { 95 System.out.println("Caught " + e.toString()); 96 // this leaves the write lock locked. 97 } 98 } 99 }); 100 t.start(); 101 t.join(); 102 103 // the next call used to freeze with the earlier ReadWriteLock lock 104 // implementation 105 aai.appendLoopOnAppenders(17); 106 } 107 }