• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2025 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import protobuf_unittest.UnittestProto;
11 import protobuf_unittest.UnittestProto.TestAllTypes;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.concurrent.CountDownLatch;
15 import java.util.concurrent.ExecutionException;
16 import java.util.concurrent.ExecutorService;
17 import java.util.concurrent.Executors;
18 import java.util.concurrent.Future;
19 import org.junit.Assert;
20 import org.junit.Test;
21 import org.junit.runner.RunWith;
22 import org.junit.runners.JUnit4;
23 
24 @RunWith(JUnit4.class)
25 public final class ConcurrentDescriptorsTest {
26   public static final int N = 1000;
27 
28   static class Worker implements Runnable {
29     private final CountDownLatch startSignal;
30     private final CountDownLatch doneSignal;
31     private final Runnable trigger;
32 
Worker(CountDownLatch startSignal, CountDownLatch doneSignal, Runnable trigger)33     Worker(CountDownLatch startSignal, CountDownLatch doneSignal, Runnable trigger) {
34       this.startSignal = startSignal;
35       this.doneSignal = doneSignal;
36       this.trigger = trigger;
37     }
38 
39     @Override
run()40     public void run() {
41       try {
42         startSignal.await();
43         trigger.run();
44       } catch (InterruptedException | RuntimeException e) {
45         doneSignal.countDown();
46         throw new RuntimeException(e); // Rethrow for main thread to handle
47       }
48       doneSignal.countDown();
49     }
50   }
51 
52   @Test
testSimultaneousStaticInit()53   public void testSimultaneousStaticInit() throws InterruptedException {
54     ExecutorService executor = Executors.newFixedThreadPool(N);
55     CountDownLatch startSignal = new CountDownLatch(1);
56     CountDownLatch doneSignal = new CountDownLatch(N);
57     List<Future<?>> futures = new ArrayList<>(N);
58     for (int i = 0; i < N; i++) {
59       Future<?> future =
60           executor.submit(
61               new Worker(
62                   startSignal,
63                   doneSignal,
64                   // Static method invocation triggers static initialization.
65                   () -> Assert.assertNotNull(UnittestProto.getDescriptor())));
66       futures.add(future);
67     }
68     startSignal.countDown();
69     doneSignal.await();
70     System.out.println("Done with all threads...");
71     for (int i = 0; i < futures.size(); i++) {
72       try {
73         futures.get(i).get();
74       } catch (ExecutionException e) {
75         Assert.fail("Thread " + i + " failed with:" + e.getMessage());
76       }
77     }
78     executor.shutdown();
79   }
80 
81   @Test
testSimultaneousFeatureAccess()82   public void testSimultaneousFeatureAccess() throws InterruptedException {
83     ExecutorService executor = Executors.newFixedThreadPool(N);
84     CountDownLatch startSignal = new CountDownLatch(1);
85     CountDownLatch doneSignal = new CountDownLatch(N);
86     List<Future<?>> futures = new ArrayList<>(N);
87     for (int i = 0; i < N; i++) {
88       Future<?> future =
89           executor.submit(
90               new Worker(
91                   startSignal,
92                   doneSignal,
93                   // hasPresence() uses the [field_presence] feature.
94                   () ->
95                       Assert.assertTrue(
96                           TestAllTypes.getDescriptor()
97                               .findFieldByName("optional_int32")
98                               .hasPresence())));
99       futures.add(future);
100     }
101     startSignal.countDown();
102     doneSignal.await();
103     System.out.println("Done with all threads...");
104     for (int i = 0; i < futures.size(); i++) {
105       try {
106         futures.get(i).get();
107       } catch (ExecutionException e) {
108         Assert.fail("Thread " + i + " failed with:" + e.getMessage());
109       }
110     }
111     executor.shutdown();
112   }
113 }
114