Add with-acquired blocks to code that needs it

This commit is contained in:
Joshua Suskalo 2021-09-27 12:09:23 -05:00
parent fb3b95977f
commit 6bc907e720

View file

@ -31,8 +31,6 @@
ResourceScope ResourceScope
SegmentAllocator))) SegmentAllocator)))
;; TODO(Joshua): Ensure all the serdes acquire the scopes they use
(defn stack-scope (defn stack-scope
"Constructs a new scope for use only in this thread. "Constructs a new scope for use only in this thread.
@ -104,15 +102,19 @@
(.keepAlive ^ResourceScope source ^ResourceScope target)) (.keepAlive ^ResourceScope source ^ResourceScope target))
(defmacro with-acquired (defmacro with-acquired
"Acquires a `scope` to ensure it will not be released until the `body` completes. "Acquires one or more `scopes` until the `body` completes.
This is only necessary to do on shared scopes, however if you are operating on This is only necessary to do on shared scopes, however if you are operating on
an arbitrary passed scope, it is best practice to wrap code that interacts an arbitrary passed scope, it is best practice to wrap code that interacts
with it wrapped in this." with it wrapped in this."
[scope & body] {:style/indent 1}
[scopes & body]
`(with-open [scope# (stack-scope)] `(with-open [scope# (stack-scope)]
(keep-alive scope# ~scope) (run! (partial keep-alive scope#) ~scopes)
~@body)) ~@body))
(s/fdef with-acquired
:args (s/cat :scopes any?
:body (s/* any?)))
(defn address-of (defn address-of
"Gets the address of a given segment. "Gets the address of a given segment.
@ -176,14 +178,16 @@
(defn copy-segment (defn copy-segment
"Copies the content to `dest` from `src`" "Copies the content to `dest` from `src`"
[dest src] [dest src]
(.copyFrom ^MemorySegment dest ^MemorySegment src)) (with-acquired (map segment-scope [src dest])
(.copyFrom ^MemorySegment dest ^MemorySegment src)))
(defn clone-segment (defn clone-segment
"Clones the content of `segment` into a new segment of the same size." "Clones the content of `segment` into a new segment of the same size."
([segment] (clone-segment segment (connected-scope))) ([segment] (clone-segment segment (connected-scope)))
([segment scope] ([segment scope]
(with-acquired [(segment-scope segment) scope]
(doto ^MemorySegment (alloc (.byteSize ^MemorySegment segment) scope) (doto ^MemorySegment (alloc (.byteSize ^MemorySegment segment) scope)
(copy-segment segment)))) (copy-segment segment)))))
(defn slice-segments (defn slice-segments
"Constructs a lazy seq of `size`-length memory segments, sliced from `segment`." "Constructs a lazy seq of `size`-length memory segments, sliced from `segment`."
@ -318,9 +322,10 @@
[obj type scope] [obj type scope]
(when-not (null? obj) (when-not (null? obj)
(if (sequential? type) (if (sequential? type)
(with-acquired [scope]
(let [segment (alloc-instance (second type) scope)] (let [segment (alloc-instance (second type) scope)]
(serialize-into obj (second type) segment scope) (serialize-into obj (second type) segment scope)
(address-of segment)) (address-of segment)))
obj))) obj)))
(defmulti serialize-into (defmulti serialize-into
@ -333,7 +338,10 @@
override [[c-layout]]. override [[c-layout]].
For any other type, this will serialize it as [[serialize*]] before writing For any other type, this will serialize it as [[serialize*]] before writing
the result value into the `segment`." the result value into the `segment`.
Implementations of this should be inside a [[with-acquired]] block for the
`scope` if they perform multiple memory operations."
(fn (fn
#_{:clj-kondo/ignore [:unused-binding]} #_{:clj-kondo/ignore [:unused-binding]}
[obj type segment scope] [obj type segment scope]
@ -342,7 +350,8 @@
(defmethod serialize-into :default (defmethod serialize-into :default
[obj type segment scope] [obj type segment scope]
(if-some [prim-layout (primitive-type type)] (if-some [prim-layout (primitive-type type)]
(serialize-into (serialize* obj type scope) prim-layout segment scope) (with-acquired [(segment-scope scope) scope]
(serialize-into (serialize* obj type scope) prim-layout segment scope))
(throw (ex-info "Attempted to serialize an object to a type that has not been overriden" (throw (ex-info "Attempted to serialize an object to a type that has not been overriden"
{:type type {:type type
:object obj})))) :object obj}))))
@ -403,7 +412,10 @@
"Deserializes the given segment into a Clojure data structure. "Deserializes the given segment into a Clojure data structure.
For types that serialize to primitives, a default implementation will For types that serialize to primitives, a default implementation will
deserialize the primitive before calling [[deserialize*]]." deserialize the primitive before calling [[deserialize*]].
Implementations of this should be inside a [[with-acquired]] block for the the
`segment`'s scope if they perform multiple memory operations."
(fn (fn
#_{:clj-kondo/ignore [:unused-binding]} #_{:clj-kondo/ignore [:unused-binding]}
[segment type] [segment type]
@ -412,9 +424,10 @@
(defmethod deserialize-from :default (defmethod deserialize-from :default
[segment type] [segment type]
(if-some [prim (primitive-type type)] (if-some [prim (primitive-type type)]
(with-acquired [(segment-scope segment)]
(-> segment (-> segment
(deserialize-from prim) (deserialize-from prim)
(deserialize* type)) (deserialize* type)))
(throw (ex-info "Attempted to deserialize a non-primitive type that has not been overriden" (throw (ex-info "Attempted to deserialize a non-primitive type that has not been overriden"
{:type type {:type type
:segment segment})))) :segment segment}))))
@ -453,8 +466,9 @@
(defmethod deserialize-from ::pointer (defmethod deserialize-from ::pointer
[segment type] [segment type]
(with-acquired [(segment-scope segment)]
(cond-> (MemoryAccess/getAddress segment) (cond-> (MemoryAccess/getAddress segment)
(sequential? type) (deserialize* type))) (sequential? type) (deserialize* type))))
(defmulti deserialize* (defmulti deserialize*
"Deserializes a primitive object into a Clojure data structure. "Deserializes a primitive object into a Clojure data structure.
@ -490,15 +504,15 @@
a segment." a segment."
[obj type] [obj type]
(when-not (identical? ::void type) (when-not (identical? ::void type)
((if (primitive-type type) (if (primitive-type type)
deserialize* (deserialize* obj type)
deserialize-from) (deserialize-from obj type))))
obj type)))
(defn seq-of (defn seq-of
"Constructs a lazy sequence of `type` elements deserialized from `segment`." "Constructs a lazy sequence of `type` elements deserialized from `segment`."
[type segment] [type segment]
(map #(deserialize % type) (slice-segments segment (size-of type)))) (with-acquired [(segment-scope segment)]
(map #(deserialize % type) (slice-segments segment (size-of type)))))
;;; C String type ;;; C String type