Compile Owl as native executable and replace client/server-architecture for...

Compile Owl as native executable and replace client/server-architecture for testing by native executable. Closes #215.
parent 108ff8e3
......@@ -24,10 +24,12 @@ Build:
script:
- apt-get update && apt-get install -y --no-install-recommends build-essential zlib1g-dev # TODO: Remove this after Docker update
- ./gradlew distZip
- build/native-executable/owl-native --version
artifacts:
paths:
- build/clibrary/reports
- build/distributions
- build/native-executable/reports
- build/native-library/reports
- build/reports
when: on_success
expire_in: 1 week
......
......@@ -28,7 +28,9 @@ PATH=/opt/graalvm-ce-java11-20.1.0/bin/:$PATH
JAVA_HOME=/opt/graalvm-ce-java11-20.1.0/
```
## Building
If GraalVM (native-image) is not available, the project can also be built with a reduced set of features on any JDK that supports at least Java 11. See below for instructions.
## Building on GraalVM
The standard distribution can be obtained with:
......@@ -36,7 +38,11 @@ The standard distribution can be obtained with:
$ ./gradlew distZip
```
The resulting `.zip` is located in `build/distributions`. It includes the scripts for the CLI tools, Jars usable as a Java library, and a C library. If only the Java components are required, you can exclude native code by executing:
The resulting `.zip` is located in `build/distributions`. It includes the scripts for the CLI tools, Jars usable as a Java library, and a C library.
## Building on OpenJDK
If GraalVM is not available, building the native executable and library can be skipped by executing:
```
$ ./gradlew distZip -Pdisable-native
......
......@@ -50,14 +50,12 @@ project.targetCompatibility = JavaVersion.VERSION_11
def defaultEncoding = 'UTF-8'
tasks.withType(JavaCompile) {
options.compilerArgs << '-Xlint:unchecked'
options.deprecation = true
options.encoding = defaultEncoding
// options.errorprone.disableWarningsInGeneratedCode = true
}
tasks.withType(Javadoc) {
options.encoding = defaultEncoding
}
def disablePandoc = project.hasProperty("disable-pandoc")
def disableNative = project.hasProperty("disable-native")
def enableNativeAssertions = project.hasProperty("enable-native-assertions")
......@@ -194,7 +192,8 @@ def scripts = [
'nba2dpa' : 'owl.translations.nba2dpa.NBA2DPA',
'nba2ldba' : 'owl.translations.nba2ldba.NBA2LDBA',
'dra2dpa' : 'owl.translations.dra2dpa.IARBuilder',
'owl-server': 'owl.run.ServerCli'
// 'owl-server': 'owl.run.ServerCli'
]
def rabinizerScripts = [
......@@ -251,9 +250,9 @@ task javadocJar(type: Jar, dependsOn: javadoc) {
task mavenJars(dependsOn: [jar, sourcesJar, javadocJar])
task buildCLibrary(type: Exec, dependsOn: [classes]) {
mkdir "${buildDir}/clibrary"
workingDir "${buildDir}/clibrary"
task buildNativeLibrary(type: Exec, dependsOn: [classes]) {
mkdir "${buildDir}/native-library"
workingDir "${buildDir}/native-library"
def graalHome = System.getenv("GRAAL_HOME")
def command = (graalHome != null ? graalHome + "/bin/" : "") + "native-image"
......@@ -273,6 +272,28 @@ task buildCLibrary(type: Exec, dependsOn: [classes]) {
'-H:-UseServiceLoaderFeature'
}
task buildNativeExecutable(type: Exec, dependsOn: [classes]) {
mkdir "${buildDir}/native-executable"
workingDir "${buildDir}/native-executable"
def graalHome = System.getenv("GRAAL_HOME")
def command = (graalHome != null ? graalHome + "/bin/" : "") + "native-image"
// TODO: remove dependency on header files.
commandLine command,
"owl.run.DefaultCli", "owl-native",
"-cp", sourceSets.main.runtimeClasspath.asPath,
enableNativeAssertions ? '-ea' : '-da',
"-DowlHeader=${projectDir}/src/main/headers",
'--initialize-at-build-time=owl,it,com,org,jhoafparser',
'--no-fallback',
'--no-server',
'-H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime',
'-H:+PrintAnalysisCallTree',
'-H:+ReportExceptionStackTraces',
'-H:-UseServiceLoaderFeature'
}
// ---------------- Script Jobs ----------------
def allStartScripts = createStartScriptTasks("", jar.outputs.files + project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME), scripts)
......@@ -333,19 +354,18 @@ distributions {
into("bin") {
from allStartScripts
if (!disableNative) {
from "${buildDir}/exe/owlClient/owl-client"
}
fileMode = 0755
}
if (!disableNative) {
into("clib") {
from "$projectDir/src/main/headers"
from "$buildDir/clibrary" include "*.h"
from "$buildDir/clibrary" include "*.so"
from "$buildDir/native-library" include "*.h"
from "$buildDir/native-library" include "*.so"
}
into("bin") {
from "$buildDir/native-executable" include "owl-native"
}
}
......@@ -373,10 +393,10 @@ distributions {
}
if (!disableNative) {
tasks.getByPath(":distZip").dependsOn(tasks.getByPath(":owlClientExecutable"))
tasks.getByPath(":distTar").dependsOn(tasks.getByPath(":owlClientExecutable"))
tasks.getByPath(":distZip").dependsOn(tasks.getByPath(":buildCLibrary"))
tasks.getByPath(":distTar").dependsOn(tasks.getByPath(":buildCLibrary"))
tasks.getByPath(":distZip").dependsOn(tasks.getByPath(":buildNativeExecutable"))
tasks.getByPath(":distTar").dependsOn(tasks.getByPath(":buildNativeExecutable"))
tasks.getByPath(":distZip").dependsOn(tasks.getByPath(":buildNativeLibrary"))
tasks.getByPath(":distTar").dependsOn(tasks.getByPath(":buildNativeLibrary"))
}
apply from: 'gradle/idea.gradle'
......@@ -145,7 +145,7 @@
"name": "delag",
"flags": {
"strict": "--fallback=none",
"fallback-spot": "--fallback=ltl2tgba -H --deterministic --generic"
"fallback-spot": "--fallback='ltl2tgba -H --deterministic --generic'"
},
"exclusive-flags": [
[
......@@ -223,7 +223,7 @@
[
"ltl2aut-ext",
"-t",
"ltl2tgba -H"
"'ltl2tgba -H'"
]
]
},
......@@ -237,7 +237,7 @@
[
"ltl2aut-ext",
"-t",
"ltl2tgba -H"
"'ltl2tgba -H'"
]
],
"post": [
......
......@@ -103,9 +103,8 @@ class OwlTool(Tool):
if os.name == 'nt':
tool_execution = ["build\\bin\\owl.bat"]
else:
tool_execution = ["build/bin/owl"]
# TODO Fix --worker flag
tool_execution.extend(["-i", static_input]) # , "--worker", "0"])
tool_execution = ["build/bin/owl-native"]
tool_execution.extend(["-i", static_input])
tool_execution.extend(self.get_pipeline())
return tool_execution
......
......@@ -95,7 +95,7 @@ def _test(args, check):
else:
raise TypeError("Unknown tools type {0!s}".format(type(tools)))
enable_server = True
enable_server = False
port = 6060
servers = {}
for test_tool in loaded_tools:
......
/*
* Copyright (C) 2016 - 2019 (See AUTHORS)
* Copyright (C) 2016 - 2020 (See AUTHORS)
*
* This file is part of Owl.
*
......@@ -31,7 +31,9 @@ import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.cli.DefaultParser;
......@@ -39,7 +41,6 @@ import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import owl.run.modules.OwlModuleRegistry;
import owl.run.parser.OwlParser;
import owl.util.DaemonThreadFactory;
public final class ServerCli {
private static final Logger logger = Logger.getLogger(ServerCli.class.getName());
......@@ -75,7 +76,7 @@ public final class ServerCli {
logger.log(Level.INFO, "Starting server on {0}:{1}", new Object[] {address, port});
ExecutorService connectionExecutor = Executors.newCachedThreadPool(
new DaemonThreadFactory(Thread.currentThread().getThreadGroup()));
new DaemonThreadFactory());
try (var socket = ServerSocketChannel.open().bind(new InetSocketAddress(address, port))) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
......@@ -130,4 +131,20 @@ public final class ServerCli {
connectionExecutor.shutdownNow();
}
}
static final class DaemonThreadFactory implements ThreadFactory {
private static final AtomicInteger factoryNumber = new AtomicInteger(1);
private final ThreadGroup group = Thread.currentThread().getThreadGroup();
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final int number = factoryNumber.getAndIncrement();
@Override
public Thread newThread(Runnable r) {
String name = String.format("owl-worker-%d-%d", number, threadNumber.getAndIncrement());
Thread t = new Thread(group, r, name);
t.setDaemon(true);
return t;
}
}
}
......@@ -87,7 +87,7 @@ abstract class DependencyTree<T> {
if (fallbackAutomaton.acceptance() instanceof AllAcceptance) {
fallbackAutomaton = OmegaAcceptanceCast.cast(fallbackAutomaton, BuchiAcceptance.class);
}
return new FallbackLeaf<>(formula, acceptanceSet, fallbackAutomaton);
}
......
......@@ -29,12 +29,11 @@ import static owl.translations.ltl2dpa.LTL2DPAFunction.Configuration.SYMMETRIC;
import com.google.common.util.concurrent.Uninterruptibles;
import java.util.EnumSet;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import javax.annotation.Nullable;
import java.util.function.Supplier;
import owl.automaton.AnnotatedStateOptimisation;
import owl.automaton.Automaton;
import owl.automaton.BooleanOperations;
......@@ -47,7 +46,6 @@ import owl.ltl.BooleanConstant;
import owl.ltl.LabelledFormula;
import owl.run.Environment;
import owl.translations.mastertheorem.Selector;
import owl.util.DaemonThreadFactory;
public class LTL2DPAFunction implements Function<LabelledFormula, Automaton<?, ParityAcceptance>> {
......@@ -77,11 +75,9 @@ public class LTL2DPAFunction implements Function<LabelledFormula, Automaton<?, P
@Override
public Automaton<?, ParityAcceptance> apply(LabelledFormula formula) {
var executor = Executors.newCachedThreadPool(
new DaemonThreadFactory(Thread.currentThread().getThreadGroup()));
Callable<Result<?>> automatonCallable;
Callable<Result<?>> complementCallable;
Supplier<Optional<Result<?>>> automatonSupplier;
Supplier<Optional<Result<?>>> complementSupplier;
if (configuration.contains(COMPLEMENT_CONSTRUCTION_HEURISTIC)) {
int fixpoints = configuration.contains(SYMMETRIC)
......@@ -93,103 +89,60 @@ public class LTL2DPAFunction implements Function<LabelledFormula, Automaton<?, P
: Selector.selectAsymmetric(formula.formula().not(), false).size();
if (fixpoints <= negationFixpoints) {
automatonCallable = configuration.contains(SYMMETRIC)
? () -> symmetricConstruction(formula)
: () -> asymmetricConstruction(formula);
complementCallable = () -> null;
automatonSupplier = configuration.contains(SYMMETRIC)
? () -> Optional.of(symmetricConstruction(formula))
: () -> Optional.of(asymmetricConstruction(formula));
complementSupplier = Optional::empty;
} else {
automatonCallable = () -> null;
complementCallable = configuration.contains(SYMMETRIC)
? () -> symmetricConstruction(formula.not())
: () -> asymmetricConstruction(formula.not());
automatonSupplier = Optional::empty;
complementSupplier = configuration.contains(SYMMETRIC)
? () -> Optional.of(symmetricConstruction(formula.not()))
: () -> Optional.of(asymmetricConstruction(formula.not()));
}
} else {
automatonCallable = configuration.contains(SYMMETRIC)
? () -> symmetricConstruction(formula)
: () -> asymmetricConstruction(formula);
automatonSupplier = configuration.contains(SYMMETRIC)
? () -> Optional.of(symmetricConstruction(formula))
: () -> Optional.of(asymmetricConstruction(formula));
if (configuration.contains(COMPLEMENT_CONSTRUCTION_EXACT)) {
complementCallable = configuration.contains(SYMMETRIC)
? () -> symmetricConstruction(formula.not())
: () -> asymmetricConstruction(formula.not());
complementSupplier = configuration.contains(SYMMETRIC)
? () -> Optional.of(symmetricConstruction(formula.not()))
: () -> Optional.of(asymmetricConstruction(formula.not()));
} else {
complementCallable = () -> null;
complementSupplier = Optional::empty;
}
}
Future<Result<?>> automatonFuture = executor.submit(automatonCallable);
Future<Result<?>> complementFuture = executor.submit(complementCallable);
// Setup Thread for complement construction.
var complementReference = new AtomicReference<Automaton<?, ParityAcceptance>>();
var complementThread = new Thread(
() -> complementSupplier.get().map(Result::complement).ifPresent(complementReference::set));
complementThread.setDaemon(true);
complementThread.start();
try {
Automaton<?, ParityAcceptance> automaton = null;
Automaton<?, ParityAcceptance> complement = null;
var automaton = automatonSupplier.get().map(x -> x.automaton).orElse(null);
Uninterruptibles.joinUninterruptibly(complementThread);
var complement = complementReference.get();
do {
if (automaton == null) {
automaton = getAutomaton(automatonFuture);
}
if (complement == null) {
complement = getComplement(complementFuture);
}
} while (!exitLoop(automaton, complement));
if (complement == null) {
assert automaton != null;
return automaton;
}
if (automaton == null) {
return complement;
}
// Select smaller automaton.
if (automaton.size() < complement.size()) {
return automaton;
}
if (automaton.size() > complement.size()) {
return complement;
}
return automaton.acceptance().acceptanceSets()
<= complement.acceptance().acceptanceSets() ? automaton : complement;
} catch (ExecutionException ex) {
// The translation broke down, it is unsafe to continue...
automatonFuture.cancel(true);
complementFuture.cancel(true);
//noinspection ProhibitedExceptionThrown
throw new RuntimeException(ex); // NOPMD
} finally {
executor.shutdown();
if (complement == null) {
return Objects.requireNonNull(automaton);
}
}
private boolean exitLoop(@Nullable Automaton<?, ?> automaton,
@Nullable Automaton<?, ?> complement) {
if (configuration.contains(COMPLEMENT_CONSTRUCTION_HEURISTIC)) {
return automaton != null || complement != null;
if (automaton == null) {
return Objects.requireNonNull(complement);
}
if (configuration.contains(COMPLEMENT_CONSTRUCTION_EXACT)) {
return automaton != null && complement != null;
// Select smaller automaton.
if (automaton.size() < complement.size()) {
return automaton;
}
return automaton != null;
}
@Nullable
private static Automaton<?, ParityAcceptance> getAutomaton(Future<Result<?>> future)
throws ExecutionException {
var result = Uninterruptibles.getUninterruptibly(future);
return result == null ? null : result.automaton;
}
if (automaton.size() > complement.size()) {
return complement;
}
@Nullable
private static Automaton<?, ParityAcceptance> getComplement(Future<Result<?>> future)
throws ExecutionException {
var result = Uninterruptibles.getUninterruptibly(future);
return result == null ? null : result.complement();
return automaton.acceptance().acceptanceSets()
<= complement.acceptance().acceptanceSets() ? automaton : complement;
}
private Result<AsymmetricRankingState> asymmetricConstruction(LabelledFormula formula) {
......
/*
* Copyright (C) 2016 - 2019 (See AUTHORS)
*
* This file is part of Owl.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package owl.util;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public final class DaemonThreadFactory implements ThreadFactory {
private static final AtomicInteger factoryNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final int number = factoryNumber.getAndIncrement();
public DaemonThreadFactory(ThreadGroup group) {
this.group = group;
}
@Override
public Thread newThread(Runnable r) {
String name = String.format("owl-worker-%d-%d", number, threadNumber.getAndIncrement());
Thread t = new Thread(group, r, name);
t.setDaemon(true);
return t;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment