This PR adds support for server-sided `RpcRef` reuse and fixes a bug where trace nodes in the InfoView would close while the file was still being processed. The core of the trace node issue is that the server always serves new RPC references in every single response to the client, which means that the client is forced to reset its UI state. In a previous attempt at fixing this (#8056), the server would memorize the RPC-encoded JSON of interactive diagnostics (which includes RPC references) and serve it for as long as it could reuse the snapshot containing the diagnostics, so that RPC references are reused. The problem with this was that the client then had multiple finalizers registered for the same RPC reference (one for every reused RPC reference that was served), and once the first reference was garbage-collected, all other reused references would point into the void. This PR takes a different approach to resolve the issue: The meaning of `$/lean/rpc/release` is relaxed from "Free the object pointed to by this RPC reference" to "Decrement the RPC reference count of the object pointed to by this RPC reference", and the server now maintains a reference count to track how often a given `RpcRef` was served. Only when every single served instance of the `RpcRef` has been released, the object is freed. Additionally, the reuse mechanism is generalized from being only supported for interactive diagnostics, to being supported for any object using `WithRpcRef`. In order to make use of reusable RPC references, downstream users still need to memorize the `WithRpcRef` instances accordingly. Closes #8053. ### Breaking changes Since `WithRpcRef` is now capable of tracking its identity to decide which `WithRpcRef` usage constitutes a reuse, the constructor of `WithRpcRef` has been made `private` to discourage downstream users from creating `WithRpcRef` instances with manually-set `id`s. Instead, `WithRpcRef.mk` (which lives in `BaseIO`) is now the preferred way to create `WithRpcRef` instances.
95 lines
2.4 KiB
Text
95 lines
2.4 KiB
Text
import Lean.Server.Rpc.Basic
|
||
|
||
open Lean Server
|
||
|
||
abbrev M := StateM RpcObjectStore
|
||
|
||
def M.run (x : ExceptT String M α) : Except String α :=
|
||
x.run' {}
|
||
|
||
def test (α : Type) [RpcEncodable α] (a : α) := M.run do
|
||
let json ← rpcEncode a
|
||
let _a : α ← ofExcept (rpcDecode json (← get))
|
||
return json
|
||
|
||
structure FooRef where
|
||
a : Array Nat
|
||
deriving Inhabited, TypeName
|
||
|
||
#eval do return test (WithRpcRef FooRef) (← WithRpcRef.mk default)
|
||
|
||
structure FooJson where
|
||
s : String
|
||
deriving FromJson, ToJson, Inhabited
|
||
|
||
structure Bar where
|
||
fooRef : WithRpcRef FooRef
|
||
fooJson : FooJson
|
||
deriving RpcEncodable, Inhabited
|
||
|
||
def Bar.mkDefault : BaseIO Bar := do return {
|
||
fooRef := ← WithRpcRef.mk default
|
||
fooJson := default
|
||
}
|
||
|
||
#eval do return test Bar (← Bar.mkDefault)
|
||
|
||
structure BarTrans where
|
||
bar : Bar
|
||
deriving RpcEncodable, Inhabited
|
||
|
||
#eval do return test BarTrans { bar := ← Bar.mkDefault }
|
||
|
||
structure Baz where
|
||
arr : Array String -- non-constant field
|
||
deriving RpcEncodable, Inhabited
|
||
|
||
#eval test Baz default
|
||
|
||
structure FooGeneric (α : Type) where
|
||
a : α
|
||
b? : Option α
|
||
deriving RpcEncodable, Inhabited
|
||
|
||
#eval test (FooGeneric Nat) default
|
||
#eval test (FooGeneric Nat) { a := 3, b? := some 42 }
|
||
|
||
inductive BazInductive
|
||
| baz (arr : Array Bar)
|
||
deriving RpcEncodable, Inhabited
|
||
|
||
#eval do return test BazInductive ⟨#[← Bar.mkDefault, ← Bar.mkDefault]⟩
|
||
|
||
inductive FooInductive (α : Type) where
|
||
| a : α → WithRpcRef FooRef → FooInductive α
|
||
| b : (n : Nat) → (a : α) → (m : Nat) → FooInductive α
|
||
deriving RpcEncodable, Inhabited
|
||
|
||
#eval do return test (FooInductive BazInductive) (.a default (← WithRpcRef.mk default))
|
||
#eval do return test (FooInductive BazInductive) (.b 42 default default)
|
||
|
||
inductive FooNested (α : Type) where
|
||
| a : α → Array (FooNested α) → FooNested α
|
||
deriving RpcEncodable, Inhabited
|
||
|
||
#eval test (FooNested BazInductive) (.a default #[default])
|
||
|
||
inductive FooParam (n : Nat) where
|
||
| a : Nat → FooParam n
|
||
deriving RpcEncodable, Inhabited
|
||
|
||
#eval test (FooParam 10) (.a 42)
|
||
|
||
inductive Unused (α : Type) | a
|
||
deriving RpcEncodable, Inhabited
|
||
|
||
structure NoRpcEncodable
|
||
#eval test (Unused NoRpcEncodable) default
|
||
|
||
structure UnusedStruct (α : Type)
|
||
deriving RpcEncodable, Inhabited
|
||
|
||
#eval test (UnusedStruct NoRpcEncodable) default
|
||
|
||
deriving instance Repr, RpcEncodable for Empty
|
||
#eval rpcDecode (α := Empty) .null {}
|