Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"fable": {
"version": "5.0.0-rc.5",
"version": "5.0.0",
"commands": [
"fable"
],
Expand All @@ -17,7 +17,7 @@
"rollForward": false
},
"easybuild.shipit": {
"version": "2.0.0",
"version": "2.3.0",
"commands": [
"shipit"
],
Expand Down
4 changes: 2 additions & 2 deletions examples/timeflies-beam/src/Timeflies.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../timeflies/src/Timeflies.Common.fsproj" />
<PackageReference Include="Fable.Beam" Version="5.0.0-rc.22" />
<PackageReference Include="Fable.Beam.Cowboy" Version="5.0.0-rc.22" />
<PackageReference Include="Fable.Beam" Version="5.0.0-rc.27" />
<PackageReference Include="Fable.Beam.Cowboy" Version="5.0.0-rc.23" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion examples/timeflies-js/src/Timeflies.Js.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<ProjectReference Include="../../timeflies/src/Timeflies.Common.fsproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fable.Core" Version="5.0.0-rc.1" />
<PackageReference Include="Fable.Core" Version="5.0.0" />
<PackageReference Include="Feliz" Version="3.2.0" />
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions examples/timeflies-python/src/Timeflies.Python.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<ProjectReference Include="../../timeflies/src/Timeflies.Common.fsproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fable.Core" Version="5.0.0-rc.1" />
<PackageReference Include="Fable.Python" Version="5.0.0-rc.2" />
<PackageReference Include="Fable.Core" Version="5.0.0" />
<PackageReference Include="Fable.Python" Version="5.0.0" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ check:

# Format source files
format:
dotnet fantomas src -r
dotnet fantomas src

# Setup tooling
restore:
Expand Down
19 changes: 9 additions & 10 deletions src/Fable.Actor/Actor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ open Fable.Actor.Platform
type ActorOp<'T> = { Run: ('T -> unit) -> unit }

type Actor<'Msg> = {
Pid: Pid
Pid: Pid<'Msg>
} with

member _.Receive() : ActorOp<'Msg> = {
Run = fun cont -> receiveMsg (fun raw -> cont (unbox<'Msg> raw))
}

member this.Post(msg: 'Msg) = sendMsg this.Pid (box msg)
member this.Post(msg: 'Msg) = sendMsg this.Pid msg

type ActorBuilder() =
member _.Bind(op: ActorOp<'T>, f: 'T -> ActorOp<'U>) : ActorOp<'U> = {
Expand Down Expand Up @@ -150,8 +150,8 @@ module Actor =
Reply = fun reply -> sendReply callerPid ref reply
}

sendMsg actor.Pid (box (msg, rc))
cont (unbox (recvReply ref))
sendMsg actor.Pid (msg, rc)
cont (recvReply ref)
}

/// Send a message and await a reply with a timeout in milliseconds.
Expand All @@ -166,10 +166,10 @@ module Actor =
Reply = fun reply -> sendReply callerPid ref reply
}

sendMsg actor.Pid (box (msg, rc))
sendMsg actor.Pid (msg, rc)

match recvReplyWithTimeout ref timeout with
| Some reply -> cont (unbox<'Reply> reply)
| Some reply -> cont reply
| None -> raise (System.TimeoutException("Actor call timed out"))
}

Expand Down Expand Up @@ -407,11 +407,10 @@ module Actor =
#if FABLE_COMPILER_BEAM

/// Schedule a timer callback. Returns a typed handle for cancellation.
let schedule (ms: int) (callback: unit -> unit) : TimerHandle =
TimerHandle(box (timerSchedule ms callback))
let schedule (ms: int) (callback: unit -> unit) : TimerHandle = TimerHandle(timerSchedule ms callback)

/// Cancel a scheduled timer.
let cancelTimer (TimerHandle handle: TimerHandle) : unit = timerCancel (unbox<Pid> handle)
let cancelTimer (TimerHandle handle: TimerHandle) : unit = timerCancel handle

#else

Expand All @@ -437,7 +436,7 @@ module Actor =

/// Extract the raw platform handle from an actor.
#if FABLE_COMPILER_BEAM
let pid (actor: Actor<'Msg>) : Pid = actor.Pid
let pid (actor: Actor<'Msg>) : Pid<'Msg> = actor.Pid
#else
let pid (actor: Actor<'Msg>) : obj = actor.Pid
#endif
4 changes: 2 additions & 2 deletions src/Fable.Actor/Fable.Actor.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fable.Beam" Version="5.0.0-rc.22" />
<PackageReference Include="Fable.Core" Version="5.0.0-rc.1" />
<PackageReference Include="Fable.Beam" Version="5.0.0-rc.27" />
<PackageReference Include="Fable.Core" Version="5.0.0" />
</ItemGroup>
</Project>
30 changes: 17 additions & 13 deletions src/Fable.Actor/Platform.fs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let private atomNormal: Atom = Erlang.binaryToAtom "normal"
// Process helpers (use Fable.Beam.Erlang with actor-specific atoms)
// ============================================================================

let killProcess (pid: Pid) : unit = Erlang.exitPid pid atomKill
let killProcess (pid: Pid<'Msg>) : unit = Erlang.exitPid pid atomKill
let exitNormal () : unit = Erlang.exit atomNormal
let trapExits () : unit = Erlang.trapExit () |> ignore
let formatReason (reason: obj) : string = Erlang.formatTerm reason
Expand All @@ -36,19 +36,19 @@ let formatReason (reason: obj) : string = Erlang.formatTerm reason
type InternalMsg =
| [<CompiledName("fable_actor_msg")>] ActorMsg of payload: obj
| [<CompiledName("fable_actor_timer")>] ActorTimer of ref: obj * callback: (obj -> unit)
| [<CompiledName("EXIT")>] Exit of pid: Pid * reason: obj
| [<CompiledName("EXIT")>] Exit of pid: Pid<obj> * reason: obj

// ============================================================================
// Message passing
// ============================================================================

/// Send a tagged user message: Pid ! {fable_actor_msg, Msg}
[<Emit("$0 ! {fable_actor_msg, $1}, ok")>]
let sendMsg (pid: Pid) (msg: obj) : unit = nativeOnly
let sendMsg (pid: Pid<'Msg>) (msg: 'Msg) : unit = nativeOnly

/// Send a tagged reply: Pid ! {fable_actor_reply, Ref, Value}
[<Emit("$0 ! {fable_actor_reply, $1, $2}, ok")>]
let sendReply (pid: Pid) (ref: Ref) (value: obj) : unit = nativeOnly
let sendReply (pid: Pid<'Caller>) (ref: Ref<'Reply>) (value: 'Reply) : unit = nativeOnly

/// CPS receive: blocks until a user message arrives.
/// Dispatches timer callbacks and EXIT signals transparently.
Expand All @@ -71,12 +71,12 @@ let rec receiveMsg (cont: obj -> unit) : unit =
/// Blocking selective receive for a reply matching a specific ref.
/// Uses emitErlExpr to preserve Erlang's bound-variable semantics —
/// only the message with the matching ref is consumed from the mailbox.
let recvReply (ref: Ref) : obj =
let recvReply (ref: Ref<'Reply>) : 'Reply =
emitErlExpr ref "receive {fable_actor_reply, $0, FableReply} -> FableReply end"

/// Selective receive for a reply with timeout.
/// Returns Some(reply) or None on timeout.
let recvReplyWithTimeout (ref: Ref) (timeout: int) : obj option =
let recvReplyWithTimeout (ref: Ref<'Reply>) (timeout: int) : 'Reply option =
emitErlExpr (ref, timeout) "receive {fable_actor_reply, $0, FableReply} -> {some, FableReply} after $1 -> undefined end"

// ============================================================================
Expand All @@ -93,14 +93,18 @@ let isChildExited (msg: obj) : bool = nativeOnly
type private TimerControl = | [<CompiledName("cancel")>] Cancel

/// Schedule a callback after ms milliseconds.
/// Returns the timer process pid for cancellation.
let timerSchedule (ms: int) (callback: unit -> unit) : Pid =
Erlang.spawn (fun () ->
match Erlang.receive<TimerControl> ms with
| Some Cancel -> ()
| None -> callback ())
/// Returns the timer process pid (boxed) for cancellation.
let timerSchedule (ms: int) (callback: unit -> unit) : obj =
let pid: Pid<TimerControl> =
Erlang.spawn (fun () ->
match Erlang.receive<TimerControl> ms with
| Some Cancel -> ()
| None -> callback ())

box pid

/// Cancel a scheduled timer by sending the cancel atom to its process.
let timerCancel (timer: Pid) : unit = Erlang.send timer (box Cancel)
let timerCancel (timer: obj) : unit =
Erlang.send (unbox<Pid<TimerControl>> timer) Cancel

#endif
2 changes: 1 addition & 1 deletion test/Test.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
<ProjectReference Include="../src/Fable.Actor/Fable.Actor.fsproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fable.Core" Version="5.0.0-rc.1" />
<PackageReference Include="Fable.Core" Version="5.0.0" />
</ItemGroup>
</Project>
Loading