Skip to content

Commit 63d7f3b

Browse files
committed
Script invokes now allocate task IDs
1 parent 38ce566 commit 63d7f3b

File tree

23 files changed

+372
-88
lines changed

23 files changed

+372
-88
lines changed

CHANGES

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
[1.8.0]
2+
- Add methods to get total scripts queued for execution
3+
- Compiled script invocations now return a task ID so that individual invokes can be cancelled
4+
- Add methods to cancel queued scripts by script or task ID
5+
- Add methods to skip running scripts by task ID
6+
17
[1.7.1]
28
- Fix interactive script running flag always being set to true. Update unit test.
39
- Reduce lock contention when interactive script queue is empty

core/src/main/java/org/mini2Dx/miniscript/core/GameScriptingEngine.java

Lines changed: 169 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public abstract class GameScriptingEngine implements Runnable {
5555

5656
public static Locks LOCK_PROVIDER = new JvmLocks();
5757

58+
private static final int DEFAULT_MAX_CONCURRENT_SCRIPTS = 2;
59+
5860
private final ScriptInvocationPool scriptInvocationPool = new ScriptInvocationPool();
5961
private final ScriptInvocationQueue scriptInvocationQueue = new ScriptInvocationQueue();
6062
final Queue<ScriptNotification> scriptNotifications = new ReadWriteArrayQueue<ScriptNotification>();
@@ -66,6 +68,8 @@ public abstract class GameScriptingEngine implements Runnable {
6668
private final Set<Integer> completedFutures = new HashSet<Integer>();
6769
private final Set<Integer> completedScripts = new HashSet<Integer>();
6870

71+
private final List<String> tmpRunningScripts = new ArrayList<>();
72+
6973
private final ThreadPoolProvider threadPoolProvider;
7074
private final ScriptExecutorPool<?> scriptExecutorPool;
7175

@@ -76,15 +80,15 @@ public abstract class GameScriptingEngine implements Runnable {
7680

7781
/**
7882
* Constructs a scripting engine backed by a thread pool with the maximum
79-
* amount of concurrent scripts set to the amount of processors + 1.
83+
* amount of concurrent scripts set to 2.
8084
* Sandboxing is enabled if the implementation supports it.
8185
*/
8286
public GameScriptingEngine() {
83-
this(Runtime.getRuntime().availableProcessors() + 1);
87+
this(DEFAULT_MAX_CONCURRENT_SCRIPTS);
8488
}
8589

8690
public GameScriptingEngine(ThreadPoolProvider threadPoolProvider) {
87-
this(Runtime.getRuntime().availableProcessors() + 1, threadPoolProvider);
91+
this(DEFAULT_MAX_CONCURRENT_SCRIPTS, threadPoolProvider);
8892
}
8993

9094
/**
@@ -118,7 +122,7 @@ public GameScriptingEngine(ClasspathScriptProvider classpathScriptProvider, int
118122
scriptExecutorPool = createScriptExecutorPool(classpathScriptProvider, maxConcurrentScripts, isSandboxingSupported());
119123

120124
threadPoolProvider = new DefaultThreadPoolProvider(maxConcurrentScripts + 1);
121-
init();
125+
init(maxConcurrentScripts);
122126
}
123127

124128
public GameScriptingEngine(ClasspathScriptProvider classpathScriptProvider, int maxConcurrentScripts, ThreadPoolProvider threadPoolProvider) {
@@ -127,7 +131,7 @@ public GameScriptingEngine(ClasspathScriptProvider classpathScriptProvider, int
127131
scriptExecutorPool = createScriptExecutorPool(classpathScriptProvider, maxConcurrentScripts, isSandboxingSupported());
128132

129133
this.threadPoolProvider = threadPoolProvider;
130-
init();
134+
init(maxConcurrentScripts);
131135
}
132136

133137
/**
@@ -147,16 +151,16 @@ public GameScriptingEngine(ThreadPoolProvider threadPoolProvider, boolean sandbo
147151

148152
/**
149153
* Constructs a scripting engine backed by a thread pool with the maximum
150-
* amount of concurrent scripts set to the amount of processors + 1.
154+
* amount of concurrent scripts set to 2.
151155
* @param classpathScriptProvider The auto-generated {@link ClasspathScriptProvider} for the game
152156
* @param sandboxed True if script sandboxing should be enabled
153157
*/
154158
public GameScriptingEngine(ClasspathScriptProvider classpathScriptProvider, boolean sandboxed) {
155-
this(classpathScriptProvider,Runtime.getRuntime().availableProcessors() + 1, sandboxed);
159+
this(classpathScriptProvider,DEFAULT_MAX_CONCURRENT_SCRIPTS, sandboxed);
156160
}
157161

158162
public GameScriptingEngine(ClasspathScriptProvider classpathScriptProvider, ThreadPoolProvider threadPoolProvider, boolean sandboxed) {
159-
this(classpathScriptProvider,Runtime.getRuntime().availableProcessors() + 1, threadPoolProvider, sandboxed);
163+
this(classpathScriptProvider,DEFAULT_MAX_CONCURRENT_SCRIPTS, threadPoolProvider, sandboxed);
160164
}
161165

162166
/**
@@ -192,7 +196,7 @@ public GameScriptingEngine(ClasspathScriptProvider classpathScriptProvider,
192196
scriptExecutorPool = createScriptExecutorPool(classpathScriptProvider, maxConcurrentScripts, sandboxed);
193197

194198
threadPoolProvider = new DefaultThreadPoolProvider(maxConcurrentScripts + 1);
195-
init();
199+
init(maxConcurrentScripts);
196200
}
197201

198202
public GameScriptingEngine(ClasspathScriptProvider classpathScriptProvider,
@@ -202,11 +206,13 @@ public GameScriptingEngine(ClasspathScriptProvider classpathScriptProvider,
202206
scriptExecutorPool = createScriptExecutorPool(classpathScriptProvider, maxConcurrentScripts, sandboxed);
203207

204208
this.threadPoolProvider = threadPoolProvider;
205-
init();
209+
init(maxConcurrentScripts);
206210
}
207211

208-
private void init() {
209-
threadPoolProvider.submit(this);
212+
private void init(int maxConcurrentScripts) {
213+
for(int i = 0; i < maxConcurrentScripts; i++) {
214+
threadPoolProvider.submit(this);
215+
}
210216
threadPoolProvider.scheduleAtFixedRate(new Runnable() {
211217
@Override
212218
public void run() {
@@ -240,6 +246,13 @@ public void dispose(boolean interruptScripts) {
240246
cleanupTask = null;
241247
}
242248
threadPoolProvider.shutdown(interruptScripts);
249+
250+
if(!interruptScripts) {
251+
return;
252+
}
253+
cancelAllQueuedScripts();
254+
skipAllQueuedGameFutures();
255+
skipAllScripts();
243256
}
244257

245258
/**
@@ -316,8 +329,8 @@ public void run() {
316329
invocationListener = scriptInvocation.getInvocationListener();
317330
}
318331

319-
ScriptExecutionTask<?> executionTask = scriptExecutorPool.execute(scriptInvocation.getScriptId(),
320-
scriptInvocation.getScriptBindings(), invocationListener);
332+
ScriptExecutionTask<?> executionTask = scriptExecutorPool.execute(scriptInvocation.getTaskId(),
333+
scriptInvocation.getScriptId(), scriptInvocation.getScriptBindings(), invocationListener);
321334
Future<?> taskFuture = threadPoolProvider.submit(executionTask);
322335
executionTask.setTaskFuture(taskFuture);
323336
runningScripts.put(executionTask.getTaskId(), executionTask);
@@ -382,7 +395,7 @@ public void skipAllScripts() {
382395
}
383396

384397
/**
385-
* Skips a currently running script
398+
* Skips all running instances of a script
386399
*
387400
* @param scriptId
388401
* The ID of the script to skip
@@ -400,6 +413,24 @@ public void skipScript(int scriptId) {
400413
}
401414
}
402415

416+
/**
417+
* Skips a specific running instance of a script
418+
*
419+
* @param taskId The ID of the task to skip
420+
*/
421+
public void skipScriptByTaskId(int taskId) {
422+
for(int otherTaskId : runningScripts.keySet()) {
423+
if(taskId != otherTaskId) {
424+
continue;
425+
}
426+
ScriptExecutionTask<?> scriptExecutionTask = runningScripts.get(taskId);
427+
if (scriptExecutionTask == null) {
428+
continue;
429+
}
430+
scriptExecutionTask.skipScript();
431+
}
432+
}
433+
403434
/**
404435
* Skips all currently running {@link GameFuture}s
405436
*/
@@ -436,6 +467,61 @@ public void cancelAllQueuedGameFutures() {
436467
queuedFutures.clear();
437468
}
438469

470+
/**
471+
* Removes all currently queued {@link GameFuture}s without sending skipFuture event
472+
*/
473+
public void cancelAllQueuedScripts() {
474+
scriptInvocationQueue.clear();
475+
}
476+
477+
/**
478+
* Removes all currently queued scripts with a given script ID
479+
*/
480+
public void cancelQueuedScript(int scriptId) {
481+
scriptInvocationQueue.cancelByScriptId(scriptId);
482+
}
483+
484+
/**
485+
* Removes a specific queued script by its task ID
486+
*/
487+
public void cancelQueuedScriptByTaskId(int taskId) {
488+
scriptInvocationQueue.cancelByTaskId(taskId);
489+
}
490+
491+
/**
492+
* Cancels a queued script or skips it if it is currently running
493+
* @param scriptId The script ID
494+
*/
495+
public void skipOrCancelScript(int scriptId) {
496+
try {
497+
skipScript(scriptId);
498+
} catch (Exception e) {
499+
e.printStackTrace();
500+
}
501+
try {
502+
cancelQueuedScript(scriptId);
503+
} catch (Exception e) {
504+
e.printStackTrace();
505+
}
506+
}
507+
508+
/**
509+
* Cancels a specific invocation of a script by its task ID, or skips it if it is currently running
510+
* @param taskId The task ID
511+
*/
512+
public void skipOrCancelScriptByTaskId(int taskId) {
513+
try {
514+
skipScriptByTaskId(taskId);
515+
} catch (Exception e) {
516+
e.printStackTrace();
517+
}
518+
try {
519+
cancelQueuedScriptByTaskId(taskId);
520+
} catch (Exception e) {
521+
e.printStackTrace();
522+
}
523+
}
524+
439525
/**
440526
* Compiles a script for execution. Note it is best to call this
441527
* sequentially before any script executions to avoid throwing a
@@ -515,9 +601,10 @@ public int compileScript(String filepath, InputStream inputStream) throws Insuff
515601
* The id of the script to run
516602
* @param scriptBindings
517603
* The variable bindings for the script
604+
* @return The unique task ID for this invocation
518605
*/
519-
public void invokeCompiledScript(int scriptId, ScriptBindings scriptBindings) {
520-
invokeCompiledScript(scriptId, scriptBindings, null);
606+
public int invokeCompiledScript(int scriptId, ScriptBindings scriptBindings) {
607+
return invokeCompiledScript(scriptId, scriptBindings, null);
521608
}
522609

523610
/**
@@ -530,10 +617,11 @@ public void invokeCompiledScript(int scriptId, ScriptBindings scriptBindings) {
530617
* @param invocationListener
531618
* A {@link ScriptInvocationListener} to list for invocation
532619
* results
620+
* @return The unique task ID for this invocation
533621
*/
534-
public void invokeCompiledScript(int scriptId, ScriptBindings scriptBindings,
622+
public int invokeCompiledScript(int scriptId, ScriptBindings scriptBindings,
535623
ScriptInvocationListener invocationListener) {
536-
invokeCompiledScript(scriptId, scriptBindings, invocationListener, 0);
624+
return invokeCompiledScript(scriptId, scriptBindings, invocationListener, 0);
537625
}
538626

539627
/**
@@ -546,10 +634,11 @@ public void invokeCompiledScript(int scriptId, ScriptBindings scriptBindings,
546634
* @param invocationListener
547635
* A {@link ScriptInvocationListener} to list for invocation results
548636
* @param priority The script execution priority (higher value = higher priority)
637+
* @return The unique task ID for this invocation
549638
*/
550-
public void invokeCompiledScript(int scriptId, ScriptBindings scriptBindings,
639+
public int invokeCompiledScript(int scriptId, ScriptBindings scriptBindings,
551640
ScriptInvocationListener invocationListener, int priority) {
552-
invokeCompiledScript(scriptId, scriptBindings, invocationListener, priority, false);
641+
return invokeCompiledScript(scriptId, scriptBindings, invocationListener, priority, false);
553642
}
554643

555644
/**
@@ -562,10 +651,13 @@ public void invokeCompiledScript(int scriptId, ScriptBindings scriptBindings,
562651
* @param invocationListener
563652
* A {@link ScriptInvocationListener} to list for invocation results
564653
* @param priority The script execution priority (higher value = higher priority)
654+
* @return The unique task ID for this invocation
565655
*/
566-
public void invokeCompiledScript(int scriptId, ScriptBindings scriptBindings,
656+
public int invokeCompiledScript(int scriptId, ScriptBindings scriptBindings,
567657
ScriptInvocationListener invocationListener, int priority, boolean interactive) {
568-
scriptInvocationQueue.offer(scriptInvocationPool.allocate(scriptId, scriptBindings, invocationListener, priority, interactive));
658+
final ScriptInvocation invocation = scriptInvocationPool.allocate(scriptId, scriptBindings, invocationListener, priority, interactive);
659+
scriptInvocationQueue.offer(invocation);
660+
return invocation.getTaskId();
569661
}
570662

571663
/**
@@ -631,37 +723,77 @@ public int invokeScript(String scriptContent, ScriptBindings scriptBindings,
631723
*
632724
* Warning: If no {@link ScriptExecutor}s are available this will block
633725
* until one is available
634-
*
635-
* @param scriptId
636-
* The script id
637-
* @param scriptBindings
638-
* The variable bindings for the script
726+
*
727+
* @param taskId The task id
728+
* @param scriptId The script id
729+
* @param scriptBindings The variable bindings for the script
639730
*/
640-
public void invokeCompiledScriptSync(int scriptId, ScriptBindings scriptBindings) {
641-
invokeCompiledScriptSync(scriptId, scriptBindings, null);
731+
public void invokeCompiledScriptSync(int taskId, int scriptId, ScriptBindings scriptBindings) {
732+
invokeCompiledScriptSync(taskId, scriptId, scriptBindings, null);
642733
}
643734

644735
/**
645736
* Executes a compiled script synchronously on the thread calling this method.
646737
*
647738
* Warning: If no {@link ScriptExecutor}s are available this will block
648739
* until one is available
649-
*
650-
* @param scriptId
651-
* The script id
652-
* @param scriptBindings
653-
* The variable bindings for the script
740+
*
741+
* @param taskId The task id
742+
* @param scriptId The script id
743+
* @param scriptBindings The variable bindings for the script
654744
* @param invocationListener
655745
* A {@link ScriptInvocationListener} to list for invocation
656746
* results
657747
*/
658-
public void invokeCompiledScriptSync(int scriptId, ScriptBindings scriptBindings,
748+
public void invokeCompiledScriptSync(int taskId, int scriptId, ScriptBindings scriptBindings,
659749
ScriptInvocationListener invocationListener) {
660-
ScriptExecutionTask<?> executionTask = scriptExecutorPool.execute(scriptId, scriptBindings, invocationListener);
750+
ScriptExecutionTask<?> executionTask = scriptExecutorPool.execute(taskId, scriptId, scriptBindings, invocationListener);
661751
runningScripts.put(executionTask.getTaskId(), executionTask);
662752
executionTask.run();
663753
}
664754

755+
/**
756+
* Returns the list of currently running scripts.
757+
*
758+
* Note: This list reference is re-used on every invocation of this method
759+
* @return An empty list if nothing running
760+
*/
761+
public List<String> getRunningScripts() {
762+
tmpRunningScripts.clear();
763+
for(int taskId : runningScripts.keySet()) {
764+
ScriptExecutionTask<?> scriptExecutionTask = runningScripts.get(taskId);
765+
if (scriptExecutionTask == null) {
766+
continue;
767+
}
768+
tmpRunningScripts.add(scriptExecutorPool.getCompiledScriptPath(scriptExecutionTask.getScriptId()));
769+
}
770+
return tmpRunningScripts;
771+
}
772+
773+
/**
774+
* Returns the total scripts (interactive + non-interactive) queued
775+
* @return 0 if none
776+
*/
777+
public int getTotalScriptsQueued() {
778+
return scriptInvocationQueue.size();
779+
}
780+
781+
/**
782+
* Returns the total interactive scripts queued
783+
* @return 0 if none
784+
*/
785+
public int getTotalInteractiveScriptsQueued() {
786+
return scriptInvocationQueue.getInteractiveScriptsQueued();
787+
}
788+
789+
/**
790+
* Returns the total non-interactive scripts queued
791+
* @return 0 if none
792+
*/
793+
public int getTotalNonInteractiveScriptsQueued() {
794+
return scriptInvocationQueue.getNonInteractiveScriptsQueued();
795+
}
796+
665797
void submitGameFuture(GameFuture gameFuture) {
666798
queuedFutures.offer(gameFuture);
667799
}

0 commit comments

Comments
 (0)