Skip to content

Commit d0b4ddc

Browse files
Fix bridge transport and platform server reliability issues.
Connect to the mobile bridge asynchronously on startup, use non-blocking reconnect retries, wrap delegated GenServer return tuples in Platform.Server, and avoid spawn_link when raising macOS windows. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent bf9752c commit d0b4ddc

3 files changed

Lines changed: 80 additions & 43 deletions

File tree

lib/desktop/backend/wx.ex

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,13 @@ defmodule Desktop.Backend.Wx do
184184
MacOS ->
185185
name = System.get_env("EMU", "beam.smp")
186186

187-
fn ->
188-
System.cmd("open", ["-a", name], stderr_to_stdout: true, parallelism: true)
189-
end
190-
|> spawn_link()
187+
spawn(fn ->
188+
try do
189+
System.cmd("open", ["-a", name], stderr_to_stdout: true, parallelism: true)
190+
rescue
191+
_ -> :ok
192+
end
193+
end)
191194

192195
_ ->
193196
# Calling setFocus on wxDirDialog segfaults on macOS — handled above.

lib/desktop/bridge/transport.ex

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -81,22 +81,34 @@ defmodule Desktop.Bridge.Transport do
8181
def init(_opts) do
8282
port = String.to_integer(System.get_env("BRIDGE_PORT", "0"))
8383

84-
{socket, send} =
85-
if port == 0 do
86-
{Desktop.Bridge.Mock, &Desktop.Bridge.Mock.send/2}
87-
else
88-
{:ok, socket} =
89-
:gen_tcp.connect(~c"127.0.0.1", port, packet: 4, active: true, mode: :binary)
84+
if port == 0 do
85+
{:ok,
86+
%__MODULE__{
87+
port: port,
88+
socket: Desktop.Bridge.Mock,
89+
send: &Desktop.Bridge.Mock.send/2
90+
}}
91+
else
92+
{:ok,
93+
%__MODULE__{
94+
port: port,
95+
socket: nil,
96+
send: &:gen_tcp.send/2
97+
}, {:continue, :connect}}
98+
end
99+
end
90100

91-
{socket, &:gen_tcp.send/2}
92-
end
101+
@impl true
102+
def handle_continue(:connect, state = %__MODULE__{port: port}) do
103+
case connect(port) do
104+
{:ok, socket} ->
105+
{:noreply, %__MODULE__{state | socket: socket}}
93106

94-
{:ok,
95-
%__MODULE__{
96-
port: port,
97-
socket: socket,
98-
send: send
99-
}}
107+
{:error, reason} ->
108+
Logger.error("Bridge connection failed on startup: #{inspect(reason)}, retrying...")
109+
Process.send_after(self(), :reconnect, 1_000)
110+
{:noreply, state}
111+
end
100112
end
101113

102114
@impl true
@@ -199,25 +211,31 @@ defmodule Desktop.Bridge.Transport do
199211

200212
def handle_info({:tcp_error, socket, reason}, state = %__MODULE__{socket: socket}) do
201213
Logger.error("Bridge connection failed: #{inspect(reason)}")
202-
{:noreply, try_reconnect(state)}
214+
Process.send_after(self(), :reconnect, 1_000)
215+
{:noreply, %__MODULE__{state | socket: nil}}
203216
end
204217

205218
def handle_info({:tcp_closed, socket}, state = %__MODULE__{socket: socket}) do
206219
Logger.error("Bridge connection closed")
207-
{:noreply, try_reconnect(state)}
220+
Process.send_after(self(), :reconnect, 1_000)
221+
{:noreply, %__MODULE__{state | socket: nil}}
208222
end
209223

210-
def handle_info(_other, state), do: {:noreply, state}
211-
212-
defp try_reconnect(state = %__MODULE__{port: port, last_url: last_url}) do
213-
case :gen_tcp.connect(~c"127.0.0.1", port, [packet: 4, active: true, mode: :binary], 1_000) do
224+
def handle_info(:reconnect, state = %__MODULE__{port: port, last_url: last_url}) do
225+
case connect(port) do
214226
{:ok, socket} ->
215227
if last_url, do: spawn(fn -> bridge_call(:wxWebView, :loadURL, [nil, last_url]) end)
216-
%__MODULE__{state | socket: socket}
228+
{:noreply, %__MODULE__{state | socket: socket}}
217229

218230
{:error, _} ->
219-
Process.sleep(1_000)
220-
try_reconnect(state)
231+
Process.send_after(self(), :reconnect, 1_000)
232+
{:noreply, state}
221233
end
222234
end
235+
236+
def handle_info(_other, state), do: {:noreply, state}
237+
238+
defp connect(port) do
239+
:gen_tcp.connect(~c"127.0.0.1", port, [packet: 4, active: true, mode: :binary], 1_000)
240+
end
223241
end

lib/desktop/platform/server.ex

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,40 +24,56 @@ defmodule Desktop.Platform.Server do
2424
@impl true
2525
def handle_info(message, s = %__MODULE__{state: state, module: module}) do
2626
if function_exported?(module, :handle_event, 2) and is_tuple(message) do
27-
case module.handle_event(message, state) do
28-
{:noreply, new_state} -> {:noreply, %{s | state: new_state}}
29-
other -> other
30-
end
27+
module.handle_event(message, state)
28+
|> wrap_result(s)
3129
else
3230
dispatch_info(message, s)
3331
end
3432
end
3533

3634
defp dispatch_info(message, s = %__MODULE__{state: state, module: module}) do
3735
if function_exported?(module, :handle_info, 2) do
38-
case module.handle_info(message, state) do
39-
{:noreply, new_state} -> {:noreply, %{s | state: new_state}}
40-
other -> other
41-
end
36+
module.handle_info(message, state)
37+
|> wrap_result(s)
4238
else
4339
{:noreply, s}
4440
end
4541
end
4642

4743
@impl true
4844
def handle_cast(message, s = %__MODULE__{state: state, module: module}) do
49-
case module.handle_cast(message, state) do
50-
{:noreply, new_state} -> {:noreply, %{s | state: new_state}}
51-
other -> other
52-
end
45+
module.handle_cast(message, state)
46+
|> wrap_result(s)
5347
end
5448

5549
@impl true
5650
def handle_call(message, from, s = %__MODULE__{state: state, module: module}) do
57-
case module.handle_call(message, from, state) do
58-
{:noreply, new_state} -> {:noreply, %{s | state: new_state}}
59-
{:reply, reply, new_state} -> {:reply, reply, %{s | state: new_state}}
60-
other -> other
51+
module.handle_call(message, from, state)
52+
|> wrap_result(s)
53+
end
54+
55+
defp wrap_result(result, s) do
56+
case result do
57+
{:reply, reply, new_state} ->
58+
{:reply, reply, %{s | state: new_state}}
59+
60+
{:reply, reply, new_state, extra} ->
61+
{:reply, reply, %{s | state: new_state}, extra}
62+
63+
{:noreply, new_state} ->
64+
{:noreply, %{s | state: new_state}}
65+
66+
{:noreply, new_state, extra} ->
67+
{:noreply, %{s | state: new_state}, extra}
68+
69+
{:stop, reason, new_state} ->
70+
{:stop, reason, %{s | state: new_state}}
71+
72+
{:stop, reason, reply, new_state} ->
73+
{:stop, reason, reply, %{s | state: new_state}}
74+
75+
other ->
76+
other
6177
end
6278
end
6379
end

0 commit comments

Comments
 (0)