Skip to content

Commit

Permalink
[Java] Change signal handling implementation so that it works on JDK …
Browse files Browse the repository at this point in the history
…24+.
  • Loading branch information
vyazelenko committed Nov 26, 2024
1 parent 1e00e98 commit ef2a529
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 222 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package org.agrona.concurrent;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;

Expand All @@ -27,14 +30,47 @@ public class ShutdownSignalBarrier
/**
* Signals the barrier will be registered for.
*/
public static final String[] SIGNAL_NAMES = { "INT", "TERM" };
private static final String[] SIGNAL_NAMES = { "INT", "TERM" };
private static final ArrayList<CountDownLatch> LATCHES = new ArrayList<>();

static
{
for (final String signalName : SIGNAL_NAMES)
try
{
final Class<?> signalClass = Class.forName("jdk.internal.misc.Signal");
final Class<?> signalHandlerClass = Class.forName("jdk.internal.misc.Signal$Handler");
final Constructor<?> signalConstructor = signalClass.getConstructor(String.class);
final Method handle = signalClass.getMethod("handle", signalClass, signalHandlerClass);

final Object handler = Proxy.newProxyInstance(
signalHandlerClass.getClassLoader(),
new Class<?>[]{ signalHandlerClass },
(proxy, method, args) ->
{
if (signalHandlerClass == method.getDeclaringClass())
{
ShutdownSignalBarrier.signalAndClearAll();
}
else if (Object.class == method.getDeclaringClass())
{
if (method.getName().equals("toString"))
{
return args[0].toString();
}
}
return null;
});


for (final String name : SIGNAL_NAMES)
{
final Object signal = signalConstructor.newInstance(name);
handle.invoke(null, signal, handler);
}
}
catch (final ReflectiveOperationException e)
{
SigInt.register(signalName, ShutdownSignalBarrier::signalAndClearAll);
throw new RuntimeException(e);
}
}

Expand Down
44 changes: 0 additions & 44 deletions agrona/src/main/java/org/agrona/concurrent/SigInt.java

This file was deleted.

60 changes: 0 additions & 60 deletions agrona/src/main/java/org/agrona/concurrent/SigIntBarrier.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2014-2024 Real Logic Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.agrona.concurrent;

import org.junit.jupiter.api.Test;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.jupiter.api.Assertions.assertEquals;

class ShutdownSignalBarrierTest
{
@Test
void signalAndAwait() throws InterruptedException
{
final ShutdownSignalBarrier barrier = new ShutdownSignalBarrier();

final AtomicLong awaitTimeNs = new AtomicLong();
final Thread thread = new Thread(() ->
{
barrier.await();
awaitTimeNs.set(System.nanoTime());
});

thread.start();

Thread.sleep(356);

assertEquals(0, awaitTimeNs.get());
final long signalTimeNs = System.nanoTime();

barrier.signal();

thread.join();
assertThat(awaitTimeNs.get(), greaterThanOrEqualTo(signalTimeNs));
}

@Test
void signalAll() throws InterruptedException
{
record Test(ShutdownSignalBarrier barrier, AtomicLong awaitTimeNs, Thread thread)
{
}

final List<Test> data = new ArrayList<>();
for (int i = 0; i < 3; i++)
{
final ShutdownSignalBarrier barrier = new ShutdownSignalBarrier();
final AtomicLong awaitTimeNs = new AtomicLong();
final Thread thread = new Thread(() ->
{
barrier.await();
awaitTimeNs.set(System.nanoTime());
});

data.add(new Test(barrier, awaitTimeNs, thread));
thread.start();
}

Thread.sleep(123);

final long signalTimeNs = System.nanoTime();

data.get(0).barrier.signalAll();

for (final Test test : data)
{
test.thread.join();
}

for (final Test test : data)
{
assertThat(test.awaitTimeNs.get(), greaterThanOrEqualTo(signalTimeNs));
}
}

public static void main(final String[] args)
{
final ShutdownSignalBarrier barrier = new ShutdownSignalBarrier();

System.out.println(Instant.now() + " awaiting...");

barrier.await();

System.out.println(Instant.now() + " shutting down...");
}
}
111 changes: 0 additions & 111 deletions agrona/src/test/java/org/agrona/concurrent/SigIntTest.java

This file was deleted.

5 changes: 1 addition & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,7 @@ subprojects {
mkdir 'build/resources/main' // Avoid Javac warning about non-existing directory
}

if (buildJavaVersion <= 23) {
options.compilerArgs.addAll(['-Xlint:all', '-Werror']) // Enable all warnings and treat them as errors
}

options.compilerArgs.addAll(['-Xlint:all', '-Werror']) // Enable all warnings and treat them as errors
options.encoding = 'UTF-8'
options.deprecation = true
}
Expand Down

0 comments on commit ef2a529

Please sign in to comment.