Skip to content

Commit d8bea92

Browse files
[9.x] Refactor scheduled event lifecycle (#39539)
* Scheduled event lifecycle refactor * Address background callbacks * Code style * StyleCI * formatting Co-authored-by: Taylor Otwell <[email protected]>
1 parent dc768b4 commit d8bea92

File tree

4 files changed

+139
-113
lines changed

4 files changed

+139
-113
lines changed

src/Illuminate/Console/Scheduling/CallbackEvent.php

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Illuminate\Support\Reflector;
77
use InvalidArgumentException;
88
use LogicException;
9+
use RuntimeException;
910
use Throwable;
1011

1112
class CallbackEvent extends Event
@@ -24,11 +25,25 @@ class CallbackEvent extends Event
2425
*/
2526
protected $parameters;
2627

28+
/**
29+
* The result of the callback's execution.
30+
*
31+
* @var mixed
32+
*/
33+
protected $result;
34+
35+
/**
36+
* The exception that was thrown when calling the callback, if any.
37+
*
38+
* @var \Throwable|null
39+
*/
40+
protected $exception;
41+
2742
/**
2843
* Create a new event instance.
2944
*
3045
* @param \Illuminate\Console\Scheduling\EventMutex $mutex
31-
* @param string $callback
46+
* @param string|callable $callback
3247
* @param array $parameters
3348
* @param \DateTimeZone|string|null $timezone
3449
* @return void
@@ -50,58 +65,64 @@ public function __construct(EventMutex $mutex, $callback, array $parameters = []
5065
}
5166

5267
/**
53-
* Run the given event.
68+
* Run the callback event.
5469
*
5570
* @param \Illuminate\Contracts\Container\Container $container
5671
* @return mixed
5772
*
58-
* @throws \Exception
73+
* @throws \Throwable
5974
*/
6075
public function run(Container $container)
6176
{
62-
if ($this->description && $this->withoutOverlapping &&
63-
! $this->mutex->create($this)) {
64-
return;
65-
}
66-
67-
$pid = getmypid();
68-
69-
register_shutdown_function(function () use ($pid) {
70-
if ($pid === getmypid()) {
71-
$this->removeMutex();
72-
}
73-
});
74-
75-
parent::callBeforeCallbacks($container);
76-
77-
try {
78-
$response = is_object($this->callback)
79-
? $container->call([$this->callback, '__invoke'], $this->parameters)
80-
: $container->call($this->callback, $this->parameters);
81-
82-
$this->exitCode = $response === false ? 1 : 0;
83-
} catch (Throwable $e) {
84-
$this->exitCode = 1;
77+
parent::run($container);
8578

86-
throw $e;
87-
} finally {
88-
$this->removeMutex();
89-
90-
parent::callAfterCallbacks($container);
79+
if ($this->exception) {
80+
throw $this->exception;
9181
}
9282

93-
return $response;
83+
return $this->result;
9484
}
9585

9686
/**
97-
* Clear the mutex for the event.
87+
* Determine if the event should skip because another process is overlapping.
88+
*
89+
* @return bool
90+
*/
91+
public function shouldSkipDueToOverlapping()
92+
{
93+
return $this->description && parent::shouldSkipDueToOverlapping();
94+
}
95+
96+
/**
97+
* Indicate that the callback should run in the background.
9898
*
9999
* @return void
100+
*
101+
* @throws \RuntimeException
100102
*/
101-
protected function removeMutex()
103+
public function runInBackground()
104+
{
105+
throw new RuntimeException('Scheduled closures can not be run in the background.');
106+
}
107+
108+
/**
109+
* Run the callback.
110+
*
111+
* @param \Illuminate\Contracts\Container\Container $container
112+
* @return int
113+
*/
114+
protected function execute($container)
102115
{
103-
if ($this->description && $this->withoutOverlapping) {
104-
$this->mutex->forget($this);
116+
try {
117+
$this->result = is_object($this->callback)
118+
? $container->call([$this->callback, '__invoke'], $this->parameters)
119+
: $container->call($this->callback, $this->parameters);
120+
121+
return $this->result === false ? 1 : 0;
122+
} catch (Throwable $e) {
123+
$this->exception = $e;
124+
125+
return 1;
105126
}
106127
}
107128

@@ -121,13 +142,7 @@ public function withoutOverlapping($expiresAt = 1440)
121142
);
122143
}
123144

124-
$this->withoutOverlapping = true;
125-
126-
$this->expiresAt = $expiresAt;
127-
128-
return $this->skip(function () {
129-
return $this->mutex->exists($this);
130-
});
145+
return parent::withoutOverlapping($expiresAt);
131146
}
132147

133148
/**
@@ -145,9 +160,21 @@ public function onOneServer()
145160
);
146161
}
147162

148-
$this->onOneServer = true;
163+
return parent::onOneServer();
164+
}
149165

150-
return $this;
166+
/**
167+
* Get the summary of the event for display.
168+
*
169+
* @return string
170+
*/
171+
public function getSummaryForDisplay()
172+
{
173+
if (is_string($this->description)) {
174+
return $this->description;
175+
}
176+
177+
return is_string($this->callback) ? $this->callback : 'Callback';
151178
}
152179

153180
/**
@@ -161,16 +188,14 @@ public function mutexName()
161188
}
162189

163190
/**
164-
* Get the summary of the event for display.
191+
* Clear the mutex for the event.
165192
*
166-
* @return string
193+
* @return void
167194
*/
168-
public function getSummaryForDisplay()
195+
protected function removeMutex()
169196
{
170-
if (is_string($this->description)) {
171-
return $this->description;
197+
if ($this->description) {
198+
parent::removeMutex();
172199
}
173-
174-
return is_string($this->callback) ? $this->callback : 'Callback';
175200
}
176201
}

src/Illuminate/Console/Scheduling/Event.php

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -188,66 +188,81 @@ public function getDefaultOutput()
188188
*
189189
* @param \Illuminate\Contracts\Container\Container $container
190190
* @return void
191+
*
192+
* @throws \Throwable
191193
*/
192194
public function run(Container $container)
193195
{
194-
if ($this->withoutOverlapping &&
195-
! $this->mutex->create($this)) {
196+
if ($this->shouldSkipDueToOverlapping()) {
196197
return;
197198
}
198199

199-
$this->runInBackground
200-
? $this->runCommandInBackground($container)
201-
: $this->runCommandInForeground($container);
200+
$exitCode = $this->start($container);
201+
202+
if (! $this->runInBackground) {
203+
$this->finish($container, $exitCode);
204+
}
202205
}
203206

204207
/**
205-
* Get the mutex name for the scheduled command.
208+
* Determine if the event should skip because another process is overlapping.
206209
*
207-
* @return string
210+
* @return bool
208211
*/
209-
public function mutexName()
212+
public function shouldSkipDueToOverlapping()
210213
{
211-
return 'framework'.DIRECTORY_SEPARATOR.'schedule-'.sha1($this->expression.$this->command);
214+
return $this->withoutOverlapping && ! $this->mutex->create($this);
212215
}
213216

214217
/**
215-
* Run the command in the foreground.
218+
* Run the command process.
216219
*
217220
* @param \Illuminate\Contracts\Container\Container $container
218-
* @return void
221+
* @return int
222+
*
223+
* @throws \Throwable
219224
*/
220-
protected function runCommandInForeground(Container $container)
225+
protected function start($container)
221226
{
222227
try {
223228
$this->callBeforeCallbacks($container);
224229

225-
$this->exitCode = Process::fromShellCommandline(
226-
$this->buildCommand(), base_path(), null, null, null
227-
)->run();
228-
229-
$this->callAfterCallbacks($container);
230-
} finally {
230+
return $this->execute($container);
231+
} catch (Throwable $exception) {
231232
$this->removeMutex();
233+
234+
throw $exception;
232235
}
233236
}
234237

235238
/**
236-
* Run the command in the background.
239+
* Run the command process.
240+
*
241+
* @param \Illuminate\Contracts\Container\Container $container
242+
* @return int
243+
*/
244+
protected function execute($container)
245+
{
246+
return Process::fromShellCommandline(
247+
$this->buildCommand(), base_path(), null, null, null
248+
)->run();
249+
}
250+
251+
/**
252+
* Mark the command process as finished and run callbacks/cleanup.
237253
*
238254
* @param \Illuminate\Contracts\Container\Container $container
255+
* @param int $exitCode
239256
* @return void
240257
*/
241-
protected function runCommandInBackground(Container $container)
258+
public function finish(Container $container, $exitCode)
242259
{
243-
try {
244-
$this->callBeforeCallbacks($container);
260+
$this->exitCode = (int) $exitCode;
245261

246-
Process::fromShellCommandline($this->buildCommand(), base_path(), null, null, null)->run();
247-
} catch (Throwable $exception) {
262+
try {
263+
$this->callAfterCallbacks($container);
264+
} finally {
248265
$this->removeMutex();
249-
250-
throw $exception;
251266
}
252267
}
253268

@@ -277,24 +292,6 @@ public function callAfterCallbacks(Container $container)
277292
}
278293
}
279294

280-
/**
281-
* Call all of the "after" callbacks for the event.
282-
*
283-
* @param \Illuminate\Contracts\Container\Container $container
284-
* @param int $exitCode
285-
* @return void
286-
*/
287-
public function callAfterCallbacksWithExitCode(Container $container, $exitCode)
288-
{
289-
$this->exitCode = (int) $exitCode;
290-
291-
try {
292-
$this->callAfterCallbacks($container);
293-
} finally {
294-
$this->removeMutex();
295-
}
296-
}
297-
298295
/**
299296
* Build the command string.
300297
*
@@ -931,6 +928,16 @@ public function preventOverlapsUsing(EventMutex $mutex)
931928
return $this;
932929
}
933930

931+
/**
932+
* Get the mutex name for the scheduled command.
933+
*
934+
* @return string
935+
*/
936+
public function mutexName()
937+
{
938+
return 'framework'.DIRECTORY_SEPARATOR.'schedule-'.sha1($this->expression.$this->command);
939+
}
940+
934941
/**
935942
* Delete the mutex for the event.
936943
*

src/Illuminate/Console/Scheduling/ScheduleFinishCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function handle(Schedule $schedule)
4949
collect($schedule->events())->filter(function ($value) {
5050
return $value->mutexName() == $this->argument('id');
5151
})->each(function ($event) {
52-
$event->callafterCallbacksWithExitCode($this->laravel, $this->argument('code'));
52+
$event->finish($this->laravel, $this->argument('code'));
5353

5454
$this->laravel->make(Dispatcher::class)->dispatch(new ScheduledBackgroundTaskFinished($event));
5555
});

0 commit comments

Comments
 (0)