1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2024, 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  
15  package ch.qos.logback.classic;
16  
17  import ch.qos.logback.core.util.Duration;
18  import org.junit.jupiter.api.BeforeEach;
19  import org.junit.jupiter.api.Test;
20  
21  import java.util.Arrays;
22  import java.util.concurrent.ScheduledExecutorService;
23  import java.util.concurrent.ScheduledFuture;
24  import java.util.concurrent.TimeUnit;
25  
26  public class Logback1551 {
27      LoggerContext lc;
28  
29      @BeforeEach
30      public void setUp() throws Exception {
31          lc = new LoggerContext();
32          lc.setName("x");
33      }
34      @Test
35      public void testConcurrentModificationScheduledTasks() {
36          ScheduledExecutorService scheduledExecutorService = lc.getScheduledExecutorService();
37          Duration duration = Duration.buildByMilliseconds(10);
38  
39          Runnable runnable = new Runnable() {
40              public void run() {
41                  try {
42                      Thread.sleep(100);
43                  } catch (InterruptedException e) {
44                      throw new RuntimeException(e);
45                  }
46              }
47          };
48          ScheduledFuture<?> scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(runnable,
49                  duration.getMilliseconds(), duration.getMilliseconds(), TimeUnit.MILLISECONDS);
50  
51          lc.addScheduledFuture(scheduledFuture);
52          int THREAD_COUNT = 20;
53          Thread[] threads = new Thread[THREAD_COUNT];
54  
55          for (int i = 0; i < THREAD_COUNT; i++) {
56              threads[i] = new Thread(new CancelRunnable(lc));
57              threads[i].start();
58          }
59  
60          Arrays.stream(threads).forEach(t-> {
61              try {
62                  t.join();
63              } catch (InterruptedException e) {
64                  throw new RuntimeException(e);
65              }
66          });
67  
68      }
69  
70      private class CancelRunnable implements Runnable {
71          LoggerContext lc;
72          public CancelRunnable(LoggerContext lc) {
73              this.lc = lc;
74          }
75  
76          @Override
77          public void run() {
78              lc.cancelScheduledTasks();
79          }
80      }
81  }