Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ProcessMaker/BpmnEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ public function setInlineTaskExecutionEnabled(bool $enabled): void

public function scheduleInlineJob(array $job): void
{
// This is a single pending-job slot; replacing it would silently skip a task.
if ($this->nextInlineJob !== null) {
throw new \LogicException('An inline BPMN job is already scheduled.');
}

$this->nextInlineJob = $job;
}

Expand Down
45 changes: 42 additions & 3 deletions ProcessMaker/Nayra/Managers/WorkflowManagerDefault.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
use ProcessMaker\Models\Process as Definitions;
use ProcessMaker\Models\ProcessRequest;
use ProcessMaker\Models\ProcessRequestToken as Token;
use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\BoundaryEventInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\CallActivityInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\EntityInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\EventDefinitionInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\ProcessInterface;
Expand Down Expand Up @@ -194,14 +196,50 @@ private function runInlineTask(Token $token, $jobClass)
]);
}

private function canRunInlineTask(Token $token): bool
/**
* Determine whether a task can safely reuse the current linear execution context.
*/
private function canRunInlineTask(Token $token, EntityInterface $element): bool
{
$instance = $token->getInstance();
$engine = $instance->getEngine();
if (!$engine->isInlineTaskExecutionEnabled()) {
return false;
}

// These execution models require isolation across requests or task instances.
if ($instance->getRawOriginal('process_collaboration_id')
|| $instance->getRawOriginal('parent_request_id')
|| $token->isMultiInstance()) {
return false;
}

// Boundary events depend on normal queued failure and event handling.
if ($element instanceof ActivityInterface && $element->getBoundaryEvents()->count() > 0) {
return false;
}

$process = $element->getProcess();
// BPMN participant metadata detects collaboration when request metadata is unavailable.
if ($process?->getProperty(ProcessInterface::BPMN_PROPERTY_PARTICIPANT)) {
return false;
}

// Keep parent tasks asynchronous before and after child-process execution.
foreach ($process?->getActivities() ?? [] as $activity) {
if ($activity instanceof CallActivityInterface) {
return false;
}
}

// Inline execution must not bypass an explicitly configured service queue.
if ($element instanceof ServiceTaskInterface) {
$configuration = json_decode($element->getProperty('config', '{}'), true) ?: [];
if (($configuration['queue'] ?? 'bpmn') !== 'bpmn') {
return false;
}
}

$activeTokens = collect($instance->getTokens())
->filter(fn ($currentToken) => !in_array(
$currentToken->getStatus(),
Expand All @@ -210,6 +248,7 @@ private function canRunInlineTask(Token $token): bool
))
->values();

// Reuse is safe only for the current token in a strictly linear state.
return $activeTokens->count() === 1
&& (string) $activeTokens->first()->getId() === (string) $token->getId();
}
Expand All @@ -224,7 +263,7 @@ public function runScripTask(ScriptTaskInterface $scriptTask, Token $token)
{
Log::info('Dispatch a script task: ' . $scriptTask->getId() . ' #' . $token->getId());

if ($this->canRunInlineTask($token)) {
if ($this->canRunInlineTask($token, $scriptTask)) {
$this->runInlineTask($token, RunScriptTask::class);
return;
}
Expand All @@ -244,7 +283,7 @@ public function runServiceTask(ServiceTaskInterface $serviceTask, Token $token)
{
Log::info('Dispatch a service task: ' . $serviceTask->getId());

if ($this->canRunInlineTask($token)) {
if ($this->canRunInlineTask($token, $serviceTask)) {
$this->runInlineTask($token, RunServiceTask::class);
return;
}
Expand Down
Loading
Loading