mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 21:01:51 +03:00
Removing need for asynchronous thread to execute ResourceManager finalizers (#6335)
While Enso runs single-threaded, its `ResourceManager` required additional asynchronous thread to execute its _"finalizers"_. What has been necessary back then is no longer needed since _GraalVM 21.1_. GraalVM now provides support for submitting `ThreadLocalAction` that gets then picked and executed via `TruffleSafepoint` locations. This PR uses such mechanism to _"inject"_ finalizer execution into already running Enso evaluation thread. Requiring more than one thread has complicated Enso's co-existence with other Truffle language. For example Graal.js is strictly singlethreaded and used to refuse (simple) co-existence with Enso. By allowing Enso to perform all its actions in a single thread, the synergy with Graal.js becomes better.
This commit is contained in:
parent
4076a64f33
commit
e47eb49ea8
@ -688,6 +688,8 @@
|
|||||||
- [One can define lazy atom fields][6151]
|
- [One can define lazy atom fields][6151]
|
||||||
- [Replace IOContexts with Execution Environment and generic Context][6171]
|
- [Replace IOContexts with Execution Environment and generic Context][6171]
|
||||||
- [Vector.sort handles incomparable types][5998]
|
- [Vector.sort handles incomparable types][5998]
|
||||||
|
- [Removing need for asynchronous thread to execute ResourceManager
|
||||||
|
finalizers][6335]
|
||||||
|
|
||||||
[3227]: https://github.com/enso-org/enso/pull/3227
|
[3227]: https://github.com/enso-org/enso/pull/3227
|
||||||
[3248]: https://github.com/enso-org/enso/pull/3248
|
[3248]: https://github.com/enso-org/enso/pull/3248
|
||||||
@ -794,6 +796,7 @@
|
|||||||
[6151]: https://github.com/enso-org/enso/pull/6151
|
[6151]: https://github.com/enso-org/enso/pull/6151
|
||||||
[6171]: https://github.com/enso-org/enso/pull/6171
|
[6171]: https://github.com/enso-org/enso/pull/6171
|
||||||
[5998]: https://github.com/enso-org/enso/pull/5998
|
[5998]: https://github.com/enso-org/enso/pull/5998
|
||||||
|
[6335]: https://github.com/enso-org/enso/pull/6335
|
||||||
|
|
||||||
# Enso 2.0.0-alpha.18 (2021-10-12)
|
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import org.junit.Assert;
|
|||||||
final class InliningBuiltinsOutNode extends Node {
|
final class InliningBuiltinsOutNode extends Node {
|
||||||
|
|
||||||
long execute(VirtualFrame frame, long a, long b) {
|
long execute(VirtualFrame frame, long a, long b) {
|
||||||
Assert.assertNotNull("VirtualFrame is always provided " + frame);
|
Assert.assertNotNull("VirtualFrame is always provided", frame);
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package org.enso.interpreter.runtime;
|
package org.enso.interpreter.runtime;
|
||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
|
import com.oracle.truffle.api.ThreadLocalAction;
|
||||||
|
import com.oracle.truffle.api.TruffleSafepoint;
|
||||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||||
import org.enso.interpreter.runtime.data.ManagedResource;
|
import org.enso.interpreter.runtime.data.ManagedResource;
|
||||||
|
|
||||||
@ -9,8 +11,10 @@ import java.lang.ref.Reference;
|
|||||||
import java.lang.ref.ReferenceQueue;
|
import java.lang.ref.ReferenceQueue;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
/** Allows the context to attach garbage collection hooks on the removal of certain objects. */
|
/** Allows the context to attach garbage collection hooks on the removal of certain objects. */
|
||||||
public class ResourceManager {
|
public class ResourceManager {
|
||||||
@ -59,7 +63,7 @@ public class ResourceManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
it.getParkedCount().decrementAndGet();
|
it.getParkedCount().decrementAndGet();
|
||||||
tryFinalize(it);
|
scheduleFinalizationAtSafepoint(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,7 +79,7 @@ public class ResourceManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Unconditional finalization – user controls the resource manually.
|
// Unconditional finalization – user controls the resource manually.
|
||||||
it.doFinalize(context);
|
it.finalizeNow(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +93,7 @@ public class ResourceManager {
|
|||||||
items.remove(resource.getPhantomReference());
|
items.remove(resource.getPhantomReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryFinalize(Item it) {
|
private void scheduleFinalizationAtSafepoint(Item it) {
|
||||||
if (it.isFlaggedForFinalization().get()) {
|
if (it.isFlaggedForFinalization().get()) {
|
||||||
if (it.getParkedCount().get() == 0) {
|
if (it.getParkedCount().get() == 0) {
|
||||||
// We already know that isFlaggedForFinalization was true at some
|
// We already know that isFlaggedForFinalization was true at some
|
||||||
@ -101,9 +105,22 @@ public class ResourceManager {
|
|||||||
// no further attempts are made.
|
// no further attempts are made.
|
||||||
boolean continueFinalizing = it.isFlaggedForFinalization().compareAndSet(true, false);
|
boolean continueFinalizing = it.isFlaggedForFinalization().compareAndSet(true, false);
|
||||||
if (continueFinalizing) {
|
if (continueFinalizing) {
|
||||||
it.doFinalize(context);
|
var futureToCancel = new AtomicReference<Future<Void>>(null);
|
||||||
|
var performFinalizeNow =
|
||||||
|
new ThreadLocalAction(false, false, true) {
|
||||||
|
@Override
|
||||||
|
protected void perform(ThreadLocalAction.Access access) {
|
||||||
|
var tmp = futureToCancel.getAndSet(null);
|
||||||
|
if (tmp == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tmp.cancel(false);
|
||||||
|
it.finalizeNow(context);
|
||||||
items.remove(it.reference);
|
items.remove(it.reference);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
futureToCancel.set(context.getEnvironment().submitThreadLocal(null, performFinalizeNow));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +141,7 @@ public class ResourceManager {
|
|||||||
}
|
}
|
||||||
if (workerThread == null || !workerThread.isAlive()) {
|
if (workerThread == null || !workerThread.isAlive()) {
|
||||||
worker.setKilled(false);
|
worker.setKilled(false);
|
||||||
workerThread = context.getEnvironment().createThread(worker);
|
workerThread = context.getEnvironment().createSystemThread(worker);
|
||||||
workerThread.start();
|
workerThread.start();
|
||||||
}
|
}
|
||||||
ManagedResource resource = new ManagedResource(object);
|
ManagedResource resource = new ManagedResource(object);
|
||||||
@ -158,7 +175,7 @@ public class ResourceManager {
|
|||||||
Item it = items.remove(key);
|
Item it = items.remove(key);
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
// Finalize unconditionally – all other threads are dead by now.
|
// Finalize unconditionally – all other threads are dead by now.
|
||||||
it.doFinalize(context);
|
it.finalizeNow(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +198,7 @@ public class ResourceManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
it.isFlaggedForFinalization().set(true);
|
it.isFlaggedForFinalization().set(true);
|
||||||
tryFinalize(it);
|
scheduleFinalizationAtSafepoint(it);
|
||||||
}
|
}
|
||||||
if (killed) {
|
if (killed) {
|
||||||
return;
|
return;
|
||||||
@ -229,18 +246,16 @@ public class ResourceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unconditionally performs the finalization action of this resource.
|
* Performs the finalization action of this resource right now. The thread must be inside of a
|
||||||
|
* context.
|
||||||
*
|
*
|
||||||
* @param context current execution context
|
* @param context current execution context
|
||||||
*/
|
*/
|
||||||
public void doFinalize(EnsoContext context) {
|
public void finalizeNow(EnsoContext context) {
|
||||||
Object p = context.getThreadManager().enter();
|
|
||||||
try {
|
try {
|
||||||
InteropLibrary.getUncached(finalizer).execute(finalizer, underlying);
|
InteropLibrary.getUncached(finalizer).execute(finalizer, underlying);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
context.getErr().println("Exception in finalizer: " + e.getMessage());
|
context.getErr().println("Exception in finalizer: " + e.getMessage());
|
||||||
} finally {
|
|
||||||
context.getThreadManager().leave(p);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +113,7 @@ class InterpreterContext(
|
|||||||
.newBuilder(LanguageInfo.ID)
|
.newBuilder(LanguageInfo.ID)
|
||||||
.allowExperimentalOptions(true)
|
.allowExperimentalOptions(true)
|
||||||
.allowAllAccess(true)
|
.allowAllAccess(true)
|
||||||
|
.allowCreateThread(false)
|
||||||
.out(output)
|
.out(output)
|
||||||
.err(err)
|
.err(err)
|
||||||
.option(RuntimeOptions.LOG_LEVEL, "WARNING")
|
.option(RuntimeOptions.LOG_LEVEL, "WARNING")
|
||||||
|
@ -81,7 +81,11 @@ class RuntimeManagementTest extends InterpreterTest {
|
|||||||
totalOut = consumeOut
|
totalOut = consumeOut
|
||||||
while (totalOut.length < expect && round < 500) {
|
while (totalOut.length < expect && round < 500) {
|
||||||
round = round + 1
|
round = round + 1
|
||||||
if (round % 10 == 0) forceGC();
|
if (round % 10 == 0) {
|
||||||
|
forceGC();
|
||||||
|
}
|
||||||
|
val res = eval("main a b = a * b").execute(7, 6)
|
||||||
|
assertResult(42)(res.asInt)
|
||||||
Thread.sleep(100)
|
Thread.sleep(100)
|
||||||
totalOut ++= consumeOut
|
totalOut ++= consumeOut
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user