Skip to content

Commit

Permalink
Virtual threads (#65)
Browse files Browse the repository at this point in the history
* Upgrading the Java version to version 21

* Adding a service for asynchronous tasks

* Moved the implementation of multithreading to AsyncService

* Removed Executor.java

* A little refactoring

* spotlessApply

* Shutting down the application in one function.

* Transferring Lock functionality to LockUtil

* AsyncService optimization

* Return interrupted thread for PeerTurnRefreshModule

* Return interrupted thread for GPGNetClient

* Executor with dependency injection

* spotlessApply

* Let all processes in executor terminate correctly, otherwise close the application.

* Returned Thread to classes with infinite loops.

* Error handling

* Fix close Ice Adapter

* Fixing shutdown after InterruptedException
  • Loading branch information
GodFuper authored Dec 5, 2024
1 parent 551a55f commit 51ea8f7
Show file tree
Hide file tree
Showing 29 changed files with 642 additions and 466 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on: [push]
jobs:
checks:
runs-on: ubuntu-latest
container: eclipse-temurin:17-jdk
container: eclipse-temurin:21-jdk
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
release:

runs-on: ubuntu-latest
container: eclipse-temurin:17-jdk
container: eclipse-temurin:21-jdk

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
A P2P connection proxy for Supreme Commander: Forged Alliance using [ICE](https://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment).

## Building
Build the project using gradle from the provided wrapper (Java 17 required).
Build the project using gradle from the provided wrapper (Java 21 required).

## JSONRPC Protocol
The `faf-ice-adapter` is controlled using a bi-directional [JSON-RPC](http://www.jsonrpc.org/specification) interface over TCP.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
group 'com.faforever'
version '1.0-SNAPSHOT'

sourceCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ apply plugin: 'com.github.johnrengelman.shadow'
group 'com.faforever'
version '1.0-SNAPSHOT'

sourceCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion ice-adapter/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ apply plugin: 'com.diffplug.spotless'
group 'com.faforever'


sourceCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21

repositories {
mavenCentral()
Expand Down
69 changes: 45 additions & 24 deletions ice-adapter/src/main/java/com/faforever/iceadapter/IceAdapter.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
package com.faforever.iceadapter;

import static com.faforever.iceadapter.debug.Debug.debug;

import com.faforever.iceadapter.debug.Debug;
import com.faforever.iceadapter.gpgnet.GPGNetServer;
import com.faforever.iceadapter.gpgnet.GameState;
import com.faforever.iceadapter.ice.GameSession;
import com.faforever.iceadapter.ice.PeerIceModule;
import com.faforever.iceadapter.rpc.RPCService;
import com.faforever.iceadapter.util.Executor;
import com.faforever.iceadapter.util.ExecutorHolder;
import com.faforever.iceadapter.util.LockUtil;
import com.faforever.iceadapter.util.TrayIcon;
import java.util.concurrent.Callable;
import lombok.extern.slf4j.Slf4j;
import picocli.CommandLine;

import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static com.faforever.iceadapter.debug.Debug.debug;

@CommandLine.Command(
name = "faf-ice-adapter",
mixinStandardHelpOptions = true,
usageHelpAutoWidth = true,
description = "An ice (RFC 5245) based network bridge between FAF client and ForgedAlliance.exe")
@Slf4j
public class IceAdapter implements Callable<Integer> {
public class IceAdapter implements Callable<Integer>, AutoCloseable {
private static IceAdapter INSTANCE;
private static String VERSION = "SNAPSHOT";
private static volatile GameSession GAME_SESSION;
Expand All @@ -29,6 +33,8 @@ public class IceAdapter implements Callable<Integer> {
private IceOptions iceOptions;

private volatile boolean running = true;
private final ExecutorService executor = ExecutorHolder.getExecutor();
private static final Lock lockGameSession = new ReentrantLock();

public static void main(String[] args) {
new CommandLine(new IceAdapter()).setUnmatchedArgumentsAllowed(true).execute(args);
Expand Down Expand Up @@ -60,6 +66,13 @@ public void start() {
debug().startupComplete();
}

@Override
public void close() {
executor.shutdown();
CompletableFuture.runAsync(executor::shutdownNow,
CompletableFuture.delayedExecutor(250, TimeUnit.MILLISECONDS));
}

public static void onHostGame(String mapName) {
log.info("onHostGame");
createGameSession();
Expand Down Expand Up @@ -113,42 +126,46 @@ public static void onDisconnectFromPeer(int remotePlayerId) {
});
}

private static synchronized void createGameSession() {
if (GAME_SESSION != null) {
GAME_SESSION.close();
GAME_SESSION = null;
}
private static void createGameSession() {
LockUtil.executeWithLock(lockGameSession, () -> {
if (GAME_SESSION != null) {
GAME_SESSION.close();
GAME_SESSION = null;
}

GAME_SESSION = new GameSession();
GAME_SESSION = new GameSession();
});
}

/**
* Triggered by losing gpgnet connection to FA.
* Closes the active Game/ICE session
*/
public static synchronized void onFAShutdown() {
if (GAME_SESSION != null) {
log.info("FA SHUTDOWN, closing everything");
GAME_SESSION.close();
GAME_SESSION = null;
// Do not put code outside of this if clause, else it will be executed multiple times
}
public static void onFAShutdown() {
LockUtil.executeWithLock(lockGameSession, () -> {
if (GAME_SESSION != null) {
log.info("FA SHUTDOWN, closing everything");
GAME_SESSION.close();
GAME_SESSION = null;
// Do not put code outside of this if clause, else it will be executed multiple times
}
});
}

/**
* Stop the ICE adapter
*/
public static void close() {
log.info("close() - stopping the adapter");

Executor.executeDelayed(500, () -> System.exit(0));
public static void close(int status) {
log.info("close() - stopping the adapter. Status: {}", status);

onFAShutdown(); // will close gameSession aswell
GPGNetServer.close();
RPCService.close();
Debug.close();
TrayIcon.close();

System.exit(0);
INSTANCE.close();
CompletableFuture.runAsync(() -> System.exit(status),
CompletableFuture.delayedExecutor(500, TimeUnit.MILLISECONDS));
}

public static int getId() {
Expand Down Expand Up @@ -183,6 +200,10 @@ public static boolean isRunning() {
return INSTANCE.running;
}

public static Executor getExecutor() {
return INSTANCE.executor;
}

public static GameSession getGameSession() {
return GAME_SESSION;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.faforever.iceadapter.debug;

import com.faforever.iceadapter.IceAdapter;
import java.lang.reflect.InvocationTargetException;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.CompletableFuture;

@Slf4j
public class Debug {
// TODO
Expand All @@ -17,6 +19,8 @@ public class Debug {

public static int RPC_PORT;

private static TelemetryDebugger telemetryDebugger;

private static final DebugFacade debugFacade = new DebugFacade();

public static void register(Debugger debugger) {
Expand All @@ -28,7 +32,8 @@ public static void remove(Debugger debugger) {
}

public static void init() {
new TelemetryDebugger(IceAdapter.getTelemetryServer(), IceAdapter.getGameId(), IceAdapter.getId());
telemetryDebugger = new TelemetryDebugger(
IceAdapter.getTelemetryServer(), IceAdapter.getGameId(), IceAdapter.getId());

// Debugger window is started and set to debugFuture when either window is requested as the info window can be
// used to open the debug window
Expand All @@ -38,25 +43,33 @@ public static void init() {
}

if (isJavaFxSupported()) {
new Thread(() -> {
CompletableFuture.runAsync(
() -> {
try {
Class.forName("com.faforever.iceadapter.debug.DebugWindow")
.getMethod("launchApplication")
.invoke(null);
} catch (InvocationTargetException e) {
log.info("DebugWindows stopped");
} catch (IllegalAccessException
| ClassNotFoundException
| NoSuchMethodException
| InvocationTargetException e) {
e.printStackTrace();
log.error("Could not create DebugWindow. Running without debug window.");
| NoSuchMethodException e) {
log.error("Could not create DebugWindow. Running without debug window.", e);
}
})
.start(); // Completes future once application started
},
IceAdapter.getExecutor());
} else {
log.info("No JavaFX support detected. Running without debug window.");
}
}

public static void close() {
if (telemetryDebugger != null) {
telemetryDebugger.close();
telemetryDebugger = null;
}
}

public static Debugger debug() {
return debugFacade;
}
Expand Down
Loading

0 comments on commit 51ea8f7

Please sign in to comment.