Skip to content

Commit b8e9152

Browse files
committed
refactor(process): orchestrate manager with collaborators
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent 6bf44bd commit b8e9152

1 file changed

Lines changed: 107 additions & 33 deletions

File tree

lib/Service/Process/ProcessManager.php

Lines changed: 107 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@
1414

1515
class ProcessManager {
1616
private const APP_CONFIG_KEY = 'process_registry';
17+
private const APP_CONFIG_HINTS_KEY = 'process_registry_hints';
18+
19+
private ProcessSignaler $signaler;
20+
private ListeningPidResolver $pidResolver;
1721

1822
public function __construct(
1923
private IAppConfig $appConfig,
2024
private LoggerInterface $logger,
25+
?ProcessSignaler $signaler = null,
26+
?ListeningPidResolver $pidResolver = null,
2127
) {
28+
$this->signaler = $signaler ?? new ProcessSignaler($logger);
29+
$this->pidResolver = $pidResolver ?? new ListeningPidResolver();
2230
}
2331

2432
/**
@@ -36,6 +44,7 @@ public function register(string $source, int $pid, array $context = []): void {
3644
'createdAt' => time(),
3745
];
3846
$this->saveRegistry($registry);
47+
$this->setSourceHint($source, $context);
3948
}
4049

4150
public function unregister(string $source, int $pid): void {
@@ -83,6 +92,16 @@ public function countRunning(string $source): int {
8392
* @param null|callable(array{pid: int, context: array<string, scalar>, createdAt: int}): bool $filter
8493
*/
8594
public function findRunningPid(string $source, ?callable $filter = null): int {
95+
$entries = $this->listRunning($source);
96+
foreach ($entries as $entry) {
97+
if ($filter !== null && !$filter($entry)) {
98+
continue;
99+
}
100+
return $entry['pid'];
101+
}
102+
103+
$this->hydrateFromFallback($source);
104+
86105
foreach ($this->listRunning($source) as $entry) {
87106
if ($filter !== null && !$filter($entry)) {
88107
continue;
@@ -96,7 +115,7 @@ public function findRunningPid(string $source, ?callable $filter = null): int {
96115
public function stopAll(string $source, int $signal = SIGTERM): int {
97116
$stopped = 0;
98117
foreach ($this->listRunning($source) as $entry) {
99-
if ($this->terminate($entry['pid'], $signal)) {
118+
if ($this->stopPid($entry['pid'], $signal)) {
100119
$stopped++;
101120
}
102121
$this->unregister($source, $entry['pid']);
@@ -105,55 +124,89 @@ public function stopAll(string $source, int $signal = SIGTERM): int {
105124
return $stopped;
106125
}
107126

108-
public function isRunning(int $pid): bool {
109-
if ($pid <= 0) {
110-
return false;
127+
public function stopPid(int $pid, int $signal = SIGTERM): bool {
128+
return $this->signaler->stopPid($pid, $signal);
129+
}
130+
131+
/**
132+
* @param array<string, scalar> $context
133+
*/
134+
public function setSourceHint(string $source, array $context): void {
135+
if ($source === '' || $context === []) {
136+
return;
111137
}
112138

113-
if (function_exists('posix_kill')) {
114-
return @posix_kill($pid, 0);
139+
$hints = $this->getHints();
140+
$hints[$source] = $context;
141+
$this->saveHints($hints);
142+
}
143+
144+
/**
145+
* @return int[]
146+
*/
147+
public function findListeningPids(int $port): array {
148+
return $this->pidResolver->findListeningPids($port);
149+
}
150+
151+
public function isRunning(int $pid): bool {
152+
return $this->signaler->isRunning($pid);
153+
}
154+
155+
private function hydrateFromFallback(string $source): void {
156+
$hint = $this->getSourceHint($source);
157+
if (!is_array($hint) || $hint === []) {
158+
return;
115159
}
116160

117-
if (!function_exists('exec')) {
118-
return false;
161+
$port = $this->getPortFromHint($hint);
162+
if ($port <= 0) {
163+
return;
119164
}
120165

121166
try {
122-
exec(sprintf('kill -0 %d 2>/dev/null', $pid), $output, $exitCode);
123-
return $exitCode === 0;
124-
} catch (\Throwable $e) {
125-
$this->logger->debug('Failed to probe process status', [
126-
'pid' => $pid,
167+
$pids = $this->findListeningPids($port);
168+
} catch (\RuntimeException $e) {
169+
$this->logger->debug('Unable to hydrate process registry from fallback', [
170+
'source' => $source,
171+
'port' => $port,
127172
'error' => $e->getMessage(),
128173
]);
129-
return false;
174+
return;
130175
}
131-
}
132176

133-
protected function terminate(int $pid, int $signal): bool {
134-
if ($pid <= 0) {
135-
return false;
177+
foreach ($pids as $pid) {
178+
if ($pid <= 0) {
179+
continue;
180+
}
181+
$this->register($source, $pid, $hint);
136182
}
183+
}
137184

138-
if (function_exists('posix_kill')) {
139-
return @posix_kill($pid, $signal);
185+
/**
186+
* @param array<string, scalar> $hint
187+
*/
188+
private function getPortFromHint(array $hint): int {
189+
if (isset($hint['port']) && is_numeric((string)$hint['port'])) {
190+
$port = (int)$hint['port'];
191+
if ($port > 0) {
192+
return $port;
193+
}
140194
}
141195

142-
if (!function_exists('exec')) {
143-
return false;
196+
if (isset($hint['uri']) && is_string($hint['uri'])) {
197+
return (int)(parse_url($hint['uri'], PHP_URL_PORT) ?? 0);
144198
}
145199

146-
try {
147-
exec(sprintf('kill -%d %d 2>/dev/null', $signal, $pid), $output, $exitCode);
148-
return $exitCode === 0;
149-
} catch (\Throwable $e) {
150-
$this->logger->debug('Failed to terminate process', [
151-
'pid' => $pid,
152-
'signal' => $signal,
153-
'error' => $e->getMessage(),
154-
]);
155-
return false;
156-
}
200+
return 0;
201+
}
202+
203+
/**
204+
* @return array<string, scalar>
205+
*/
206+
private function getSourceHint(string $source): array {
207+
$hints = $this->getHints();
208+
$hint = $hints[$source] ?? [];
209+
return is_array($hint) ? $hint : [];
157210
}
158211

159212
/**
@@ -175,4 +228,25 @@ private function getRegistry(): array {
175228
private function saveRegistry(array $registry): void {
176229
$this->appConfig->setValueString(Application::APP_ID, self::APP_CONFIG_KEY, json_encode($registry));
177230
}
231+
232+
/**
233+
* @return array<string, array<string, scalar>>
234+
*/
235+
private function getHints(): array {
236+
$raw = $this->appConfig->getValueString(Application::APP_ID, self::APP_CONFIG_HINTS_KEY, '{}');
237+
$decoded = json_decode($raw, true);
238+
if (!is_array($decoded)) {
239+
return [];
240+
}
241+
242+
return $decoded;
243+
}
244+
245+
/**
246+
* @param array<string, array<string, scalar>> $hints
247+
*/
248+
private function saveHints(array $hints): void {
249+
$this->appConfig->setValueString(Application::APP_ID, self::APP_CONFIG_HINTS_KEY, json_encode($hints));
250+
}
251+
178252
}

0 commit comments

Comments
 (0)