WIP Update coffi.mem to JDK 21

This commit is contained in:
Joshua Suskalo 2023-12-27 09:14:44 -06:00
parent 36f1685718
commit 2325abf53b
No known key found for this signature in database
GPG key ID: 9B6BA586EFF1B9F0

View file

@ -23,11 +23,11 @@
[clojure.spec.alpha :as s]) [clojure.spec.alpha :as s])
(:import (:import
(java.lang.foreign (java.lang.foreign
Addressable AddressLayout
MemoryAddress Arena
MemoryLayout MemoryLayout
MemorySegment MemorySegment
MemorySession MemorySegment$Scope
SegmentAllocator SegmentAllocator
ValueLayout ValueLayout
ValueLayout$OfByte ValueLayout$OfByte
@ -36,50 +36,79 @@
ValueLayout$OfLong ValueLayout$OfLong
ValueLayout$OfChar ValueLayout$OfChar
ValueLayout$OfFloat ValueLayout$OfFloat
ValueLayout$OfDouble ValueLayout$OfDouble)
ValueLayout$OfAddress)
(java.lang.ref Cleaner) (java.lang.ref Cleaner)
(java.nio ByteOrder))) (java.nio ByteOrder)))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
(defn stack-session (defn confined-arena
"Constructs a new arena for use only in this thread.
The memory allocated within this arena is cheap to allocate, like a native
stack.
The memory allocated within this arena will be cleared once it is closed, so
it is usually a good idea to create it in a [[with-open]] clause."
(^Arena []
(Arena/ofConfined)))
(defn ^:deprecated stack-session
"Constructs a new session for use only in this thread. "Constructs a new session for use only in this thread.
The memory allocated within this session is cheap to allocate, like a native The memory allocated within this session is cheap to allocate, like a native
stack." stack."
(^MemorySession [] (^Arena []
(MemorySession/openConfined)) (confined-arena))
(^MemorySession [^Cleaner cleaner] (^Arena [^Cleaner cleaner]
(MemorySession/openConfined cleaner))) (assert false "Stack sessions with associated cleaners have been removed.")))
(defn ^:deprecated stack-scope (defn ^:deprecated stack-scope
"Constructs a new scope for use only in this thread. "Constructs a new scope for use only in this thread.
The memory allocated within this scope is cheap to allocate, like a native The memory allocated within this scope is cheap to allocate, like a native
stack." stack."
^MemorySession [] ^Arena []
(stack-session)) (confined-arena))
(defn shared-session (defn shared-arena
"Constructs a new shared memory arena.
This arena can be shared across threads and memory allocated in it will only
be cleaned up once any thread accessing the arena closes it."
(^Arena []
(Arena/ofShared)))
(defn ^:deprecated shared-session
"Constructs a new shared memory session. "Constructs a new shared memory session.
This session can be shared across threads and memory allocated in it will only This session can be shared across threads and memory allocated in it will only
be cleaned up once every thread accessing the session closes it." be cleaned up once every thread accessing the session closes it."
(^MemorySession [] (^Arena []
(MemorySession/openShared)) (shared-arena))
(^MemorySession [^Cleaner cleaner] (^Arena [^Cleaner cleaner]
(MemorySession/openShared cleaner))) (assert false "Shared sessions with associated cleaners have been removed.")))
(defn ^:deprecated shared-scope (defn ^:deprecated shared-scope
"Constructs a new shared scope. "Constructs a new shared scope.
This scope can be shared across threads and memory allocated in it will only This scope can be shared across threads and memory allocated in it will only
be cleaned up once every thread accessing the scope closes it." be cleaned up once every thread accessing the scope closes it."
^MemorySession [] ^Arena []
(shared-session)) (shared-arena))
(defn connected-session (defn auto-arena
"Constructs a new memory arena that is managed by the garbage collector.
The arena may be shared across threads, and all resources created with it will
be cleaned up at the same time, when all references have been collected.
This type of arena cannot be closed, and therefore should not be created in
a [[with-open]] clause."
^Arena []
(Arena/ofAuto))
(defn ^:deprecated connected-session
"Constructs a new memory session to reclaim all connected resources at once. "Constructs a new memory session to reclaim all connected resources at once.
The session may be shared across threads, and all resources created with it The session may be shared across threads, and all resources created with it
@ -87,8 +116,8 @@
This type of session cannot be closed, and therefore should not be created in This type of session cannot be closed, and therefore should not be created in
a [[with-open]] clause." a [[with-open]] clause."
^MemorySession [] ^Arena []
(MemorySession/openImplicit)) (auto-arena))
(defn ^:deprecated connected-scope (defn ^:deprecated connected-scope
"Constructs a new scope to reclaim all connected resources at once. "Constructs a new scope to reclaim all connected resources at once.
@ -98,18 +127,28 @@
This type of scope cannot be closed, and therefore should not be created in This type of scope cannot be closed, and therefore should not be created in
a [[with-open]] clause." a [[with-open]] clause."
^MemorySession [] ^Arena []
(connected-session)) (auto-arena))
(defn global-session (defn global-arena
"Constructs the global arena, which will never reclaim its resources.
This arena may be shared across threads, but is intended mainly in cases where
memory is allocated with [[alloc]] but is either never freed or whose
management is relinquished to a native library, such as when returned from a
callback."
^Arena []
(Arena/global))
(defn ^:deprecated global-session
"Constructs the global session, which will never reclaim its resources. "Constructs the global session, which will never reclaim its resources.
This session may be shared across threads, but is intended mainly in cases This session may be shared across threads, but is intended mainly in cases
where memory is allocated with [[alloc]] but is either never freed or whose where memory is allocated with [[alloc]] but is either never freed or whose
management is relinquished to a native library, such as when returned from a management is relinquished to a native library, such as when returned from a
callback." callback."
^MemorySession [] ^Arena []
(MemorySession/global)) (global-arena))
(defn ^:deprecated global-scope (defn ^:deprecated global-scope
"Constructs the global scope, which will never reclaim its resources. "Constructs the global scope, which will never reclaim its resources.
@ -118,17 +157,17 @@
memory is allocated with [[alloc]] but is either never freed or whose memory is allocated with [[alloc]] but is either never freed or whose
management is relinquished to a native library, such as when returned from a management is relinquished to a native library, such as when returned from a
callback." callback."
^MemorySession [] ^Arena []
(global-session)) (global-arena))
(defn session-allocator (defn ^:deprecated session-allocator
"Constructs a segment allocator from the given `session`. "Constructs a segment allocator from the given `session`.
This is primarily used when working with unwrapped downcall functions. When a This is primarily used when working with unwrapped downcall functions. When a
downcall function returns a non-primitive type, it must be provided with an downcall function returns a non-primitive type, it must be provided with an
allocator." allocator."
^SegmentAllocator [^MemorySession session] ^SegmentAllocator [^Arena session]
(SegmentAllocator/newNativeArena session)) (assert false "Segment allocators can no longer be constructed from sessions."))
(defn ^:deprecated scope-allocator (defn ^:deprecated scope-allocator
"Constructs a segment allocator from the given `scope`. "Constructs a segment allocator from the given `scope`.
@ -136,26 +175,26 @@
This is primarily used when working with unwrapped downcall functions. When a This is primarily used when working with unwrapped downcall functions. When a
downcall function returns a non-primitive type, it must be provided with an downcall function returns a non-primitive type, it must be provided with an
allocator." allocator."
^SegmentAllocator [^MemorySession scope] ^SegmentAllocator [^Arena scope]
(session-allocator scope)) (assert false "Segment allocators can no longer be constructed from scopes."))
(defn segment-session (defn ^:deprecated segment-session
"Gets the memory session used to construct the `segment`." "Gets the memory session used to construct the `segment`."
^MemorySession [segment] ^Arena [^MemorySegment segment]
(.session ^MemorySegment segment)) (assert false "Segment sessions no longer provide linkes to the arenas that allocated them."))
(defn ^:deprecated segment-scope (defn segment-scope
"Gets the scope used to construct the `segment`." "Gets the scope associated with the `segment`."
^MemorySession [segment] ^MemorySegment$Scope [segment]
(segment-session segment)) (.scope ^MemorySegment segment))
(defn alloc (defn alloc
"Allocates `size` bytes. "Allocates `size` bytes.
If a `session` is provided, the allocation will be reclaimed when it is closed." If an `arena` is provided, the allocation will be reclaimed when it is closed."
(^MemorySegment [size] (alloc size (connected-session))) (^MemorySegment [size] (alloc size (auto-arena)))
(^MemorySegment [size session] (MemorySegment/allocateNative (long size) ^MemorySession session)) (^MemorySegment [size arena] (.allocate ^Arena arena (long size)))
(^MemorySegment [size alignment session] (MemorySegment/allocateNative (long size) (long alignment) ^MemorySession session))) (^MemorySegment [size alignment arena] (.allocate ^Arena arena (long size) (long alignment))))
(defn alloc-with (defn alloc-with
"Allocates `size` bytes using the `allocator`." "Allocates `size` bytes using the `allocator`."
@ -164,7 +203,7 @@
(^MemorySegment [allocator size alignment] (^MemorySegment [allocator size alignment]
(.allocate ^SegmentAllocator allocator (long size) (long alignment)))) (.allocate ^SegmentAllocator allocator (long size) (long alignment))))
(defmacro with-acquired (defmacro ^:deprecated with-acquired
"Acquires one or more `sessions` until the `body` completes. "Acquires one or more `sessions` until the `body` completes.
This is only necessary to do on shared sessions, however if you are operating This is only necessary to do on shared sessions, however if you are operating
@ -172,17 +211,7 @@
interacts with it wrapped in this." interacts with it wrapped in this."
{:style/indent 1} {:style/indent 1}
[sessions & body] [sessions & body]
(if (seq sessions) (assert false "Support was removed for keeping a shared arena open."))
`(let [session# ~(first sessions)
res# (volatile! ::invalid-value)]
(.whileAlive
^MemorySession session#
(^:once fn* []
(vreset! res#
(with-acquired [~@(rest sessions)]
~@body))))
@res#)
`(do ~@body)))
(s/fdef with-acquired (s/fdef with-acquired
:args (s/cat :sessions any? :args (s/cat :sessions any?
:body (s/* any?))) :body (s/* any?)))
@ -191,20 +220,20 @@
"Gets the address of a given segment. "Gets the address of a given segment.
This value can be used as an argument to functions which take a pointer." This value can be used as an argument to functions which take a pointer."
^MemoryAddress [addressable] ^long [addressable]
(.address ^Addressable addressable)) (.address ^MemorySegment addressable))
(defn null? (defn null?
"Checks if a memory address is null." "Checks if a memory address is null."
[addr] [addr]
(or (.equals (MemoryAddress/NULL) addr) (not addr))) (or (.equals (MemorySegment/NULL) addr) (not addr)))
(defn address? (defn address?
"Checks if an object is a memory address. "Checks if an object is a memory address.
`nil` is considered an address." `nil` is considered an address."
[addr] [addr]
(or (nil? addr) (instance? MemoryAddress addr))) (or (nil? addr) (instance? MemorySegment addr)))
(defn slice (defn slice
"Get a slice over the `segment` with the given `offset`." "Get a slice over the `segment` with the given `offset`."
@ -213,24 +242,28 @@
(^MemorySegment [segment offset size] (^MemorySegment [segment offset size]
(.asSlice ^MemorySegment segment (long offset) (long size)))) (.asSlice ^MemorySegment segment (long offset) (long size))))
(defn slice-into (defn ^:deprecated slice-into
"Get a slice into the `segment` starting at the `address`." "Get a slice into the `segment` starting at the `address`."
(^MemorySegment [address segment] (^MemorySegment [address segment]
(.asSlice ^MemorySegment segment ^MemoryAddress address)) (.asSlice ^MemorySegment segment (address-of address)))
(^MemorySegment [address segment size] (^MemorySegment [address segment size]
(.asSlice ^MemorySegment segment ^MemoryAddress address (long size)))) (.asSlice ^MemorySegment segment (address-of address) (long size))))
(defn with-offset (defn ^:deprecated with-offset
"Get a new address `offset` from the old `address`." "Get a new address `offset` from the old `address`."
^MemoryAddress [address offset] ^MemorySegment [address offset]
(.addOffset ^MemoryAddress address (long offset))) (slice address offset))
;; TODO(Joshua): Figure out if this can be replicated with [[Cleaner]]
#_
(defn add-close-action! (defn add-close-action!
"Adds a 0-arity function to be run when the `session` closes." "Adds a 0-arity function to be run when the `session` closes."
[^MemorySession session ^Runnable action] [^MemorySession session ^Runnable action]
(.addCloseAction session action) (.addCloseAction session action)
nil) nil)
;; TODO(Joshua): Determine if this needs to exist at all
#_
(defn as-segment (defn as-segment
"Dereferences an `address` into a memory segment associated with the `session`." "Dereferences an `address` into a memory segment associated with the `session`."
(^MemorySegment [^MemoryAddress address size] (^MemorySegment [^MemoryAddress address size]
@ -243,9 +276,7 @@
Returns `dest`." Returns `dest`."
^MemorySegment [^MemorySegment dest ^MemorySegment src] ^MemorySegment [^MemorySegment dest ^MemorySegment src]
(with-acquired [(segment-session src) (segment-session dest)] (.copyFrom dest src))
(.copyFrom dest src)
dest))
(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."
@ -721,12 +752,12 @@
([segment value] ([segment value]
`(let [segment# ~segment `(let [segment# ~segment
value# ~value] value# ~value]
(.set ^MemorySegment segment# ^ValueLayout$OfAddress pointer-layout 0 ^Addressable value#))) (.set ^MemorySegment segment# ^ValueLayout$OfAddress pointer-layout 0 ^MemorySegment value#)))
([segment offset value] ([segment offset value]
`(let [segment# ~segment `(let [segment# ~segment
offset# ~offset offset# ~offset
value# ~value] value# ~value]
(.set ^MemorySegment segment# ^ValueLayout$OfAddress pointer-layout offset# ^Addressable value#))))} (.set ^MemorySegment segment# ^ValueLayout$OfAddress pointer-layout offset# ^MemorySegment value#))))}
(^MemoryAddress [^MemorySegment segment ^MemoryAddress value] (^MemoryAddress [^MemorySegment segment ^MemoryAddress value]
(.set segment ^ValueLayout$OfAddress pointer-layout 0 value)) (.set segment ^ValueLayout$OfAddress pointer-layout 0 value))
(^MemoryAddress [^MemorySegment segment ^long offset ^MemoryAddress value] (^MemoryAddress [^MemorySegment segment ^long offset ^MemoryAddress value]