-
Notifications
You must be signed in to change notification settings - Fork 212
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Skip main-thread tasks if peripheral is detached
Due to the asynchronous nature of main-thread tasks, it's possible for them to be executed on peripherals which have been detached. This has been known for a long time (#893 was opened back in 2021), but finding a good solution here is tricky. Most of the time the method will silently succeed, but if we try to interact with an IComputerAccess (such as in inventory methods, as seen in #1750), we throw a NotAttachedException exception and spam the logs! This is an initial step towards fixing this - when calling a peripheral method via peripheral.call/modem.callRemote, we now wrap any enqueued main-thread tasks and silently skip them if the peripheral has been detached since. This means that peripheral methods may start to return nil when they didn't before. I think this is *fine* (though not ideal for sure!) - we return nil if the peripheral has been detached, so it's largely equivalent to that.
- Loading branch information
Showing
4 changed files
with
100 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
projects/core/src/main/java/dan200/computercraft/core/computer/GuardedLuaContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers | ||
// | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package dan200.computercraft.core.computer; | ||
|
||
import dan200.computercraft.api.lua.ILuaContext; | ||
import dan200.computercraft.api.lua.LuaException; | ||
import dan200.computercraft.api.lua.LuaTask; | ||
|
||
/** | ||
* A {@link ILuaContext} which checks if context is valid when before executing | ||
* {@linkplain #issueMainThreadTask(LuaTask) main-thread tasks}. | ||
*/ | ||
public final class GuardedLuaContext implements ILuaContext { | ||
private final ILuaContext original; | ||
private final Guard guard; | ||
|
||
public GuardedLuaContext(ILuaContext original, Guard guard) { | ||
this.original = original; | ||
this.guard = guard; | ||
} | ||
|
||
/** | ||
* Determine if this {@link GuardedLuaContext} wraps another context. | ||
* <p> | ||
* This may be used to avoid constructing new guarded contexts, in a pattern something like: | ||
* | ||
* <pre>{@code | ||
* var contextWrapper = this.contextWrapper; | ||
* if(contextWrapper == null || !contextWrapper.wraps(context)) { | ||
* contextWrapper = this.contextWrapper = new GuardedLuaContext(context, this); | ||
* } | ||
* }</pre> | ||
* | ||
* @param context The original context. | ||
* @return Whether {@code this} wraps {@code context}. | ||
*/ | ||
public boolean wraps(ILuaContext context) { | ||
return original == context; | ||
} | ||
|
||
@Override | ||
public long issueMainThreadTask(LuaTask task) throws LuaException { | ||
return original.issueMainThreadTask(() -> guard.checkValid() ? task.execute() : null); | ||
} | ||
|
||
/** | ||
* The function which checks if the context is still valid. | ||
*/ | ||
@FunctionalInterface | ||
public interface Guard { | ||
boolean checkValid(); | ||
} | ||
} |