33namespace Amp \Parallel \Context \Internal ;
44
55use Amp \Cancellation ;
6- use Amp \CancelledException ;
76use Amp \ForbidCloning ;
87use Amp \ForbidSerialization ;
8+ use Amp \Future ;
99use Amp \Parallel \Context \Context ;
1010use Amp \Parallel \Context \ContextException ;
1111use Amp \Sync \Channel ;
1212use Amp \Sync \ChannelException ;
13- use Amp \TimeoutCancellation ;
13+ use function Amp \async ;
1414use function Amp \Parallel \Context \flattenArgument ;
1515
1616/**
@@ -24,32 +24,47 @@ abstract class AbstractContext implements Context
2424 use ForbidCloning;
2525 use ForbidSerialization;
2626
27+ /** @var Future<ExitResult> */
28+ private readonly Future $ result ;
29+
2730 protected function __construct (
28- private readonly Channel $ channel ,
31+ private readonly Channel $ ipcChannel ,
32+ private readonly Channel $ resultChannel ,
2933 ) {
34+ $ this ->result = async (static function () use ($ resultChannel , $ ipcChannel ): ExitResult {
35+ try {
36+ $ data = $ resultChannel ->receive ();
37+ } catch (\Throwable $ exception ) {
38+ throw new ContextException ("Failed to receive result from context " , previous: $ exception );
39+ } finally {
40+ $ resultChannel ->close ();
41+ $ ipcChannel ->close ();
42+ }
43+
44+ if (!$ data instanceof ExitResult) {
45+ throw new ContextException (\sprintf (
46+ "The context sent data instead of exiting: %s " ,
47+ flattenArgument ($ data ),
48+ ));
49+ }
50+
51+ return $ data ;
52+ });
53+
54+ $ this ->result ->ignore ();
3055 }
3156
3257 public function receive (?Cancellation $ cancellation = null ): mixed
3358 {
3459 try {
35- $ data = $ this ->channel ->receive ($ cancellation );
60+ $ data = $ this ->ipcChannel ->receive ($ cancellation );
3661 } catch (ChannelException $ exception ) {
37- try {
38- $ data = $ this ->join (new TimeoutCancellation (0.1 ));
39- } catch (ChannelException |CancelledException ) {
40- if (!$ this ->isClosed ()) {
41- $ this ->close ();
42- }
43- throw new ContextException (
44- "The context stopped responding, potentially due to a fatal error or calling exit " ,
45- previous: $ exception ,
46- );
47- }
62+ $ this ->ipcChannel ->close ();
4863
49- throw new ContextException (\sprintf (
50- ' Context unexpectedly exited when waiting to receive data with result: %s ' ,
51- flattenArgument ( $ data ) ,
52- ), previous: $ exception ) ;
64+ throw new ContextException (
65+ " The context stopped responding, potentially due to a fatal error or calling exit " ,
66+ previous: $ exception ,
67+ );
5368 }
5469
5570 if (!$ data instanceof ContextMessage) {
@@ -74,77 +89,35 @@ public function receive(?Cancellation $cancellation = null): mixed
7489 public function send (mixed $ data ): void
7590 {
7691 try {
77- $ this ->channel ->send ($ data );
92+ $ this ->ipcChannel ->send ($ data );
7893 } catch (ChannelException $ exception ) {
79- try {
80- $ data = $ this ->join (new TimeoutCancellation (0.1 ));
81- } catch (ChannelException |CancelledException ) {
82- if (!$ this ->isClosed ()) {
83- $ this ->close ();
84- }
85-
86- throw new ContextException (
87- "The context stopped responding, potentially due to a fatal error or calling exit " ,
88- previous: $ exception ,
89- );
90- }
94+ $ this ->ipcChannel ->close ();
9195
92- throw new ContextException (\sprintf (
93- ' Context unexpectedly exited when sending data with result: %s ' ,
94- flattenArgument ( $ data ) ,
95- ), 0 , $ exception ) ;
96+ throw new ContextException (
97+ " The context stopped responding, potentially due to a fatal error or calling exit " ,
98+ previous: $ exception ,
99+ );
96100 }
97101 }
98102
99103 public function close (): void
100104 {
101- $ this ->channel ->close ();
105+ $ this ->ipcChannel ->close ();
106+ $ this ->resultChannel ->close ();
102107 }
103108
104109 public function isClosed (): bool
105110 {
106- return $ this ->channel ->isClosed ();
111+ return $ this ->resultChannel ->isClosed ();
107112 }
108113
109114 public function onClose (\Closure $ onClose ): void
110115 {
111- $ this ->channel ->onClose ($ onClose );
116+ $ this ->resultChannel ->onClose ($ onClose );
112117 }
113118
114119 protected function receiveExitResult (?Cancellation $ cancellation = null ): ExitResult
115120 {
116- if ($ this ->channel ->isClosed ()) {
117- throw new ContextException ("The context has already closed without providing a result " );
118- }
119-
120- try {
121- $ data = $ this ->channel ->receive ($ cancellation );
122- } catch (CancelledException $ exception ) {
123- throw $ exception ;
124- } catch (\Throwable $ exception ) {
125- if (!$ this ->isClosed ()) {
126- $ this ->close ();
127- }
128- throw new ContextException ("Failed to receive result from context " , previous: $ exception );
129- }
130-
131- if (!$ data instanceof ExitResult) {
132- if (!$ this ->isClosed ()) {
133- $ this ->close ();
134- }
135-
136- if ($ data instanceof ContextMessage) {
137- $ data = $ data ->getMessage ();
138- }
139-
140- throw new ContextException (\sprintf (
141- "The context sent data instead of exiting: %s " ,
142- flattenArgument ($ data ),
143- ));
144- }
145-
146- $ this ->channel ->close ();
147-
148- return $ data ;
121+ return $ this ->result ->await ($ cancellation );
149122 }
150123}
0 commit comments