From 844df6974f270dd636fedeac188048e74b1bfcbd Mon Sep 17 00:00:00 2001 From: Chris Nuernberger Date: Tue, 20 Feb 2024 09:45:39 -0700 Subject: [PATCH] Support for jdk-21 --- src/tech/v3/datatype/casting.clj | 123 +++---- src/tech/v3/datatype/datetime_api.clj | 4 +- src/tech/v3/datatype/ffi.clj | 17 +- src/tech/v3/datatype/ffi/mmodel_jdk21.clj | 348 ++++++++++++++++++ .../ffi/native_buffer_mmodel_jdk21.clj | 45 +++ .../v3/datatype/ffi/nio_buf_mmodel_jdk21.clj | 22 ++ src/tech/v3/datatype/mmap.clj | 39 +- src/tech/v3/datatype/mmap/mmodel_jdk21.clj | 58 +++ src/tech/v3/datatype/native_buffer.clj | 4 - src/tech/v3/datatype/nio_buffer.clj | 15 +- 10 files changed, 574 insertions(+), 101 deletions(-) create mode 100644 src/tech/v3/datatype/ffi/mmodel_jdk21.clj create mode 100644 src/tech/v3/datatype/ffi/native_buffer_mmodel_jdk21.clj create mode 100644 src/tech/v3/datatype/ffi/nio_buf_mmodel_jdk21.clj create mode 100644 src/tech/v3/datatype/mmap/mmodel_jdk21.clj diff --git a/src/tech/v3/datatype/casting.clj b/src/tech/v3/datatype/casting.clj index a3397398..7d415e87 100644 --- a/src/tech/v3/datatype/casting.clj +++ b/src/tech/v3/datatype/casting.clj @@ -1,6 +1,7 @@ (ns tech.v3.datatype.casting (:refer-clojure :exclude [cast]) (:require [clojure.set :as c-set] + [ham-fisted.lazy-noncaching :as lznc] [clj-commons.primitive-math :as pmath] [tech.v3.datatype.protocols :as dtype-proto] [tech.v3.datatype.errors :as errors]) @@ -15,7 +16,9 @@ (set! *warn-on-reflection* true) (set! *unchecked-math* :warn-on-boxed) -(declare rebuild-valid-datatypes!) + +(defonce ^{:tag Set} valid-datatype-set (ConcurrentHashMap/newKeySet)) +(defn- add-valid-datatype [dtype] (.add valid-datatype-set dtype)) (defonce aliased-datatypes (ConcurrentHashMap.)) @@ -24,7 +27,7 @@ "Alias a new datatype to a base datatype. Only useful for primitive datatypes" [new-dtype old-dtype] (.put ^Map aliased-datatypes new-dtype old-dtype) - (rebuild-valid-datatypes!)) + (add-valid-datatype new-dtype)) (defn un-alias-datatype @@ -43,7 +46,7 @@ ([datatype klass implement-protocols?] (.put datatype->class-map datatype klass) (.put class->datatype-map klass datatype) - (rebuild-valid-datatypes!) + (add-valid-datatype datatype) (when implement-protocols? (clojure.core/extend klass dtype-proto/PElemwiseDatatype @@ -73,36 +76,25 @@ :int32 :uint32 :int64 :uint64}) -(def unsigned-signed (c-set/map-invert signed-unsigned)) - -(def float-types #{:float32 :float64}) - -(def int-types (set (concat (flatten (seq signed-unsigned))))) +(defmacro cdef + "Eval the code at compile time and just output constant - saves initialization time." + [name code] + (let [v (eval code)] + `(def ~name ~v))) -(def signed-int-types (set (keys signed-unsigned))) +(cdef unsigned-signed (c-set/map-invert signed-unsigned)) -(def unsigned-int-types (set (vals signed-unsigned))) +(cdef float-types #{:float32 :float64}) -(def host-numeric-types (set (concat signed-int-types float-types))) +(cdef int-types (set (concat (flatten (seq signed-unsigned))))) -(def numeric-types (set (concat host-numeric-types unsigned-int-types))) +(cdef signed-int-types (set (keys signed-unsigned))) +(cdef unsigned-int-types (set (vals signed-unsigned))) -(defonce valid-datatype-set (atom nil)) +(cdef host-numeric-types (set (concat signed-int-types float-types))) -(defn ->hash-set - [data] - (doto (HashSet.) - (.addAll data))) - -(defn rebuild-valid-datatypes! - [] - (reset! valid-datatype-set - (->> (concat (keys datatype->class-map) - (keys aliased-datatypes) - numeric-types - [:object :boolean :char]) - (->hash-set)))) +(cdef numeric-types (set (concat host-numeric-types unsigned-int-types))) (defn base-host-datatype? @@ -121,8 +113,24 @@ (defn valid-datatype? [datatype] - (or (base-host-datatype? datatype) - (.contains ^Set @valid-datatype-set datatype))) + (case datatype + :int64 true + :float64 true + :int32 true + :int8 true + :int16 true + :uint8 true + :uint16 true + :uint32 true + :uint64 true + :char true + :float32 true + :boolean true + :object true + :string true + :keyword true + :uuid true + (.contains valid-datatype-set datatype))) (defn ensure-valid-datatype @@ -131,7 +139,7 @@ (throw (Exception. (format "Invalid datatype: %s" datatype))))) -(def primitive-types (set (concat numeric-types [:boolean]))) +(cdef primitive-types (set (concat numeric-types [:boolean]))) (defn- set->hash-set @@ -141,16 +149,16 @@ retval)) -(def base-host-datatypes (set (concat host-numeric-types +(cdef base-host-datatypes (set (concat host-numeric-types [:object :boolean]))) -(def base-datatypes (set (concat host-numeric-types +(cdef base-datatypes (set (concat host-numeric-types unsigned-int-types [:boolean :char :object]))) -(def ^{:tag HashSet} base-host-datatypes-hashset - (set->hash-set base-host-datatypes)) +(cdef ^{:tag HashSet} base-host-datatypes-hashset + (set->hash-set base-host-datatypes)) (defn int-width @@ -407,9 +415,9 @@ [] (keys @*cast-table*)) -(def all-host-datatypes - (set (concat host-numeric-types - [:boolean :object]))) +(cdef all-host-datatypes + (set (concat host-numeric-types + [:boolean :object]))) (alias-datatype! :double :float64) @@ -573,9 +581,6 @@ -(def type-tree (atom {})) - - (defn- set-conj [container item] (if container @@ -583,28 +588,22 @@ #{item})) -(defn add-type-pair - [child-type parent-type] - (swap! type-tree update child-type set-conj parent-type)) - - -(def default-type-pairs - [[:boolean :int8] - [:int8 :int16] - [:uint8 :int16] - [:int16 :int32] - [:uint16 :int32] - [:int32 :int64] - [:uint32 :int64] - [:int64 :float64] - [:uint64 :float64] - [:float32 :float64] - [:int16 :float32] - [:uint16 :float32]]) - - -(doseq [[child parent] default-type-pairs] - (add-type-pair child parent)) +(cdef type-tree + (reduce (fn [m [child-type parent-type]] + (update m child-type set-conj parent-type)) + {} + [[:boolean :int8] + [:int8 :int16] + [:uint8 :int16] + [:int16 :int32] + [:uint16 :int32] + [:int32 :int64] + [:uint32 :int64] + [:int64 :float64] + [:uint64 :float64] + [:float32 :float64] + [:int16 :float32] + [:uint16 :float32]])) (def type-path->root @@ -621,7 +620,7 @@ ([datatype] (->> (conj - (type-path->root datatype [] @type-tree) + (type-path->root datatype [] type-tree) :object) (reverse) (distinct) diff --git a/src/tech/v3/datatype/datetime_api.clj b/src/tech/v3/datatype/datetime_api.clj index ec23faea..2f225821 100644 --- a/src/tech/v3/datatype/datetime_api.clj +++ b/src/tech/v3/datatype/datetime_api.clj @@ -19,9 +19,7 @@ A general outline is: - datetime->milliseconds, milliseconds->datetime - plus-temporal-amount, minus-temporal-amount, long-temporal-amount - between" - (:require [tech.v3.datatype.datetime.constants] - [tech.v3.datatype.datetime.operations] - [tech.v3.datatype.export-symbols + (:require [tech.v3.datatype.export-symbols :refer [export-symbols] :as export-symbols])) diff --git a/src/tech/v3/datatype/ffi.clj b/src/tech/v3/datatype/ffi.clj index e7fe09c4..8099859c 100644 --- a/src/tech/v3/datatype/ffi.clj +++ b/src/tech/v3/datatype/ffi.clj @@ -408,7 +408,8 @@ Finally - on my favorite topic, efficiency, dtype-next has extremely fast copies * :jna - namespace `''tech.v3.datatype.ffi.jna/ffi-fns` - available if JNA version 5+ is in the classpath." [ffi-kwd] (case ffi-kwd - :jdk (reset! ffi-impl* @(requiring-resolve 'tech.v3.datatype.ffi.mmodel-jdk19/ffi-fns)) + :jdk-21 (reset! ffi-impl* @(requiring-resolve 'tech.v3.datatype.ffi.mmodel-jdk21/ffi-fns)) + :jdk-19 (reset! ffi-impl* @(requiring-resolve 'tech.v3.datatype.ffi.mmodel-jdk19/ffi-fns)) :jdk-pre-19 (reset! ffi-impl* @(requiring-resolve 'tech.v3.datatype.ffi.mmodel/ffi-fns)) :jna (reset! ffi-impl* @(requiring-resolve 'tech.v3.datatype.ffi.jna/ffi-fns)))) @@ -422,15 +423,19 @@ Finally - on my favorite topic, efficiency, dtype-next has extremely fast copies (catch Throwable e (log/debugf e "Failed to load JNA FFI implementation") (try - (set-ffi-impl! :jdk) + (set-ffi-impl! :jdk-21) (catch Throwable e - (log/debugf "Failed to load JDK 19 FFI implementation: %s" e) + (log/debugf "Failed to load JDK 21 FFI implementation: %s" e) (try - (set-ffi-impl! :jdk-pre-19) + (set-ffi-impl! :jdk-19) (catch Throwable e - (log/error e "Failed to find a suitable FFI implementation. + (log/debugf "Failed to load JDK 19 FFI implementation: %s" e) + (try + (set-ffi-impl! :jdk-pre-19) + (catch Throwable e + (log/error e "Failed to find a suitable FFI implementation. Attempted :jdk, :jdk-pre-19, and :jna -- call set-ffi-impl! from the repl to see specific failure.)") - (reset! ffi-impl* :failed)))))))) + (reset! ffi-impl* :failed)))))))))) @ffi-impl*) diff --git a/src/tech/v3/datatype/ffi/mmodel_jdk21.clj b/src/tech/v3/datatype/ffi/mmodel_jdk21.clj new file mode 100644 index 00000000..88b4545e --- /dev/null +++ b/src/tech/v3/datatype/ffi/mmodel_jdk21.clj @@ -0,0 +1,348 @@ +(ns tech.v3.datatype.ffi.mmodel-jdk21 + (:require [tech.v3.datatype.errors :as errors] + [tech.v3.datatype.ffi :as ffi] + [tech.v3.datatype.ffi.base :as ffi-base] + [tech.v3.datatype.ffi.ptr-value :as ptr-value] + [tech.v3.datatype.ffi.size-t :as ffi-size-t] + [tech.v3.datatype.ffi.libpath :as libpath]) + (:import [clojure.lang Keyword] + [java.lang.foreign + FunctionDescriptor Linker Linker$Option MemoryLayout Arena MemorySegment + SymbolLookup ValueLayout] + [java.lang.invoke MethodHandle MethodHandles MethodType] + [java.nio.file Path Paths] + [java.util ArrayList] + [tech.v3.datatype.ffi Pointer Library])) + +(set! *warn-on-reflection* true) + +(defn ptr-value + ^MemorySegment [item] + (MemorySegment/ofAddress (ptr-value/ptr-value item))) + +(defn ptr-value-q + ^MemorySegment [item] + (MemorySegment/ofAddress (ptr-value/ptr-value? item))) + +(extend-protocol ffi/PToPointer + MemorySegment + (convertible-to-pointer? [item] true) + (->pointer [item] + (Pointer. (.address item) {:src-ptr item}))) + +(defn ->path + ^Path [str-base & args] + (Paths/get (str str-base) (into-array String args))) + +(defn load-library + ^SymbolLookup [libname] + (cond + (instance? SymbolLookup libname) + libname + (instance? Path libname) + (do (System/load (.toString ^Path libname)) + (SymbolLookup/loaderLookup)) + (string? libname) + (libpath/load-library! (fn [libname] + (let [libname (str libname)] + (if (or (.contains libname "/") + (.contains libname "\\")) + (System/load libname) + (System/loadLibrary libname)) + (SymbolLookup/loaderLookup))) + #(instance? SymbolLookup %) + libname) + (nil? libname) + (.defaultLookup (Linker/nativeLinker)) + :else + (errors/throwf "Unrecognized libname type %s" (type libname)))) + +(defn find-symbol + (^MemorySegment [libname symbol-name] + (let [symbol-name (cond + (symbol? symbol-name) (name symbol-name) + (keyword? symbol-name) (name symbol-name) + :else (str symbol-name))] + (-> (load-library libname) + (.find symbol-name) + (.orElseThrow)))) + (^MemorySegment [symbol-name] + (find-symbol nil symbol-name))) + +(defn memory-layout-array + ^"[Ljava.lang.foreign.MemoryLayout;" [& args] + (into-array MemoryLayout args)) + +(defn argtype->mem-layout-type + [argtype] + (case (ffi-size-t/lower-type argtype) + :int8 ValueLayout/JAVA_BYTE + :int16 ValueLayout/JAVA_SHORT + :int32 ValueLayout/JAVA_INT + :int64 ValueLayout/JAVA_LONG + :float32 ValueLayout/JAVA_FLOAT + :float64 ValueLayout/JAVA_DOUBLE + :pointer? ValueLayout/ADDRESS + :pointer ValueLayout/ADDRESS)) + +(defn sig->fdesc + ^FunctionDescriptor [{:keys [rettype argtypes]}] + (if (or (= :void rettype) (nil? rettype)) + (FunctionDescriptor/ofVoid + (->> argtypes + (map argtype->mem-layout-type) + (apply memory-layout-array))) + (FunctionDescriptor/of + (argtype->mem-layout-type rettype) + (->> argtypes + (map argtype->mem-layout-type) + (apply memory-layout-array))))) + +(defn argtype->cls + ^Class [argtype] + (case (ffi-size-t/lower-type argtype) + :int8 Byte/TYPE + :int16 Short/TYPE + :int32 Integer/TYPE + :int64 Long/TYPE + :float32 Float/TYPE + :float64 Double/TYPE + :pointer MemorySegment + :pointer? MemorySegment + :string MemorySegment + :void Void/TYPE + (do + (errors/when-not-errorf + (instance? Class argtype) + "argtype (%s) must be instance of class" + argtype) + argtype))) + +(defn sig->method-type + ^MethodType [{:keys [rettype argtypes]}] + (let [^"[Ljava.lang.Class;" cls-ary (->> argtypes + (map argtype->cls) + (into-array Class))] + (MethodType/methodType (argtype->cls rettype) cls-ary))) + +(defn library-sym-method-handle + ^MethodHandle [library symbol-name rettype argtypes] + (let [sym (find-symbol library symbol-name) + sig {:rettype rettype + :argtypes argtypes} + fndesc (sig->fdesc sig) + ;methoddesc (sig->method-type sig) + linker (Linker/nativeLinker)] + (.downcallHandle linker sym #_methoddesc fndesc))) + +(defn emit-lib-constructor + [fn-defs] + (->> + (concat + [[:aload 0] + [:invokespecial :super :init [:void]]] + [[:aload 0] + [:aload 1] + [:invokestatic 'tech.v3.datatype.ffi.mmodel_jdk19$load_library + 'invokeStatic [Object Object]] + [:checkcast SymbolLookup] + [:putfield :this "libraryImpl" SymbolLookup]] + ;;Load all the method handles. + (mapcat + (fn [[fn-name {:keys [rettype argtypes]}]] + (let [hdl-name (str (name fn-name) "_hdl")] + (concat + [[:aload 0] ;;this-ptr + [:aload 1] ;;libname + [:ldc (name fn-name)] + [:ldc (name rettype)] + [:invokestatic Keyword "intern" [String Keyword]] + [:new ArrayList] + [:dup] + [:invokespecial ArrayList :init [:void]] + [:astore 2]] + (mapcat (fn [argtype] + [[:aload 2] + [:ldc (name argtype)] + [:invokestatic Keyword "intern" [String Keyword]] + [:invokevirtual ArrayList 'add [Object :boolean]] + [:pop]]) + argtypes) + [[:aload 2] + [:invokestatic 'tech.v3.datatype.ffi.mmodel_jdk19$library_sym_method_handle + 'invokeStatic + [Object Object Object Object Object]] + [:checkcast MethodHandle] + [:putfield :this hdl-name MethodHandle]]))) + fn-defs) + [[:aload 0] + [:dup] + [:invokevirtual :this "buildFnMap" [Object]] + [:putfield :this "fnMap" Object] + [:return]]) + (vec))) + + +(defn emit-find-symbol + [] + [[:aload 0] + [:getfield :this "libraryImpl" SymbolLookup] + [:aload 1] + [:invokestatic 'tech.v3.datatype.ffi.mmodel_jdk21$find_symbol + 'invokeStatic [Object Object Object]] + [:checkcast MemorySegment] + [:invokeinterface MemorySegment 'address [:long]] + [:invokestatic Pointer 'constructNonZero [:long Pointer]] + [:areturn]]) + + +(def ptr-cast + [[:invokestatic 'tech.v3.datatype.ffi.mmodel_jdk21$ptr_value + 'invokeStatic [Object Object]] + [:checkcast MemorySegment]]) + +(def ptr?-cast + [[:invokestatic 'tech.v3.datatype.ffi.mmodel_jdk21$ptr_value_q + 'invokeStatic [Object Object]] + [:checkcast MemorySegment]]) + +(def ptr-return + (ffi-base/ptr-return + [[:invokeinterface MemorySegment "address" [:long]]])) + +(defn emit-fn-def + [hdl-name rettype argtypes] + (->> (concat + [[:aload 0] + [:getfield :this hdl-name MethodHandle]] + (ffi-base/load-ffi-args ptr-cast ptr?-cast argtypes) + [[:invokevirtual MethodHandle "invokeExact" + (concat (map (partial ffi-base/argtype->insn + MemorySegment + :ptr-as-platform) + argtypes) + [(ffi-base/argtype->insn MemorySegment + :ptr-as-platform + rettype)])]] + (ffi-base/exact-type-retval + rettype + (fn [_ptr-type] + ptr-return))) + (vec))) + +(defn define-mmodel-library + [classname fn-defs _symbols _options] + [{:name classname + :flags #{:public} + :interfaces [Library] + :fields (->> (concat + [{:name "fnMap" + :type Object + :flags #{:public :final}} + {:name "libraryImpl" + :type SymbolLookup + :flags #{:public :final}}] + (map (fn [[fn-name _fn-args]] + {:name (str (name fn-name) "_hdl") + :type MethodHandle + :flags #{:public :final}}) + fn-defs)) + (vec)) + :methods + (->> (concat + [{:name :init + :flags #{:public} + :desc [String :void] + :emit (emit-lib-constructor fn-defs)} + {:name :findSymbol + :flags #{:public} + :desc [String Pointer] + :emit (emit-find-symbol)} + (ffi-base/emit-library-fn-map classname fn-defs) + {:name :deref + :desc [Object] + :emit [[:aload 0] + [:getfield :this "fnMap" Object] + [:areturn]]}] + (map + (fn [[fn-name fn-data]] + (let [hdl-name (str (name fn-name) "_hdl") + {:keys [rettype argtypes]} fn-data] + {:name fn-name + :flags #{:public} + :desc (concat (map (partial ffi-base/argtype->insn + MemorySegment + :ptr-as-obj) + argtypes) + [(ffi-base/argtype->insn MemorySegment + :ptr-as-ptr + rettype)]) + :emit (emit-fn-def hdl-name rettype argtypes)})) + fn-defs)) + (vec))}]) + +(defn define-library + [fn-defs symbols + {:keys [classname] + :as options}] + (let [clsname (or classname (str "tech.v3.datatype.ffi.mmodel." (name (gensym))))] + (ffi-base/define-library fn-defs + symbols + clsname + define-mmodel-library + (or (:instantiate? options) + (not (boolean classname))) + options))) + +(defn platform-ptr->ptr + [arg-idx] + [[:aload arg-idx] + [:invokeinterface MemorySegment "toRawLongValue" [:long]] + [:invokestatic Pointer "constructNonZero" [:long Pointer]]]) + +(defn define-foreign-interface + [rettype argtypes options] + (let [classname (or (:classname options) + (symbol (str "tech.v3.datatype.ffi.mmodel.ffi_" + (name (gensym))))) + retval (ffi-base/define-foreign-interface classname + rettype + argtypes + {:src-ns-str "tech.v3.datatype.ffi.mmodel" + :platform-ptr->ptr platform-ptr->ptr + :ptr->platform-ptr + (partial ffi-base/ptr->platform-ptr + "tech.v3.datatype.ffi.mmodel" + MemorySegment) + :ptrtype MemorySegment}) + iface-cls (:foreign-iface-class retval) + lookup (MethodHandles/lookup) + sig {:rettype rettype + :argtypes argtypes}] + (assoc retval + :method-handle (.findVirtual lookup + iface-cls + "invoke" + (sig->method-type + {:rettype rettype + :argtypes argtypes})) + :fndesc (sig->fdesc sig)))) + +(defn foreign-interface-instance->c + [iface-def inst] + (let [linker (Linker/nativeLinker) + new-hdn (.bindTo ^MethodHandle (:method-handle iface-def) inst) + mem-seg (.upcallStub + linker + new-hdn + ^FunctionDescriptor (:fndesc iface-def) + (Arena/global) + (make-array Linker$Option 0))] + (ffi/->pointer mem-seg))) + +(def ffi-fns + {:load-library load-library + :find-symbol find-symbol + :define-library define-library + :define-foreign-interface define-foreign-interface + :foreign-interface-instance->c foreign-interface-instance->c}) diff --git a/src/tech/v3/datatype/ffi/native_buffer_mmodel_jdk21.clj b/src/tech/v3/datatype/ffi/native_buffer_mmodel_jdk21.clj new file mode 100644 index 00000000..b6516fb8 --- /dev/null +++ b/src/tech/v3/datatype/ffi/native_buffer_mmodel_jdk21.clj @@ -0,0 +1,45 @@ +(ns tech.v3.datatype.ffi.native-buffer-mmodel-jdk21 + (:require [tech.v3.datatype.protocols :as dtype-proto] + [tech.v3.datatype.errors :as errors] + [tech.v3.datatype.native-buffer :as native-buffer] + [tech.v3.datatype.casting :as casting]) + (:import [java.lang.foreign MemorySegment] + [tech.v3.datatype.native_buffer NativeBuffer])) + + +(set! *warn-on-reflection* true) + + +(defn memory-segment->native-buffer + (^NativeBuffer [^MemorySegment mseg options] + (let [addr (.address mseg) + endianness (:endianness options (dtype-proto/platform-endianness))] + (native-buffer/wrap-address addr (.byteSize mseg) :int8 endianness mseg))) + (^NativeBuffer [mseg] + (memory-segment->native-buffer mseg nil))) + + +(extend-type MemorySegment + dtype-proto/PDatatype + (datatype [item] :memory-segment) + dtype-proto/PElemwiseDatatype + (elemwise-datatype [item] :int8) + dtype-proto/PECount + (ecount [item] (.byteSize item)) + dtype-proto/PToNativeBuffer + (convertible-to-native-buffer? [item] true) + (->native-buffer [item] (memory-segment->native-buffer item))) + + +(defn native-buffer->memory-segment + ^MemorySegment [item] + (errors/when-not-errorf + (dtype-proto/convertible-to-native-buffer? item) + "Item type (%s) is not convertible to native buffer" + (type item)) + (let [^NativeBuffer nbuf (dtype-proto/->native-buffer item) + n-bytes (* (dtype-proto/ecount nbuf) + (casting/numeric-byte-width + (dtype-proto/elemwise-datatype nbuf)))] + (-> (MemorySegment/ofAddress (.-address nbuf)) + (.reinterpret n-bytes)))) diff --git a/src/tech/v3/datatype/ffi/nio_buf_mmodel_jdk21.clj b/src/tech/v3/datatype/ffi/nio_buf_mmodel_jdk21.clj new file mode 100644 index 00000000..a22ef0ae --- /dev/null +++ b/src/tech/v3/datatype/ffi/nio_buf_mmodel_jdk21.clj @@ -0,0 +1,22 @@ +(ns tech.v3.datatype.ffi.nio-buf-mmodel-jdk21 + (:require [tech.v3.datatype.protocols :as dtype-proto]) + (:import [java.lang.foreign MemorySegment] + [java.nio ByteBuffer ByteOrder])) + + + +(set! *warn-on-reflection* true) + + +(defn direct-buffer-constructor + ^ByteBuffer [nbuf ^long address ^long nbytes _options] + (let [retval (if-not (== 0 nbytes) + (-> (MemorySegment/ofAddress address) + (.reinterpret nbytes) + (.asByteBuffer)) + (ByteBuffer/allocateDirect 0)) + endianness (dtype-proto/endianness nbuf)] + (case endianness + :little-endian (.order retval ByteOrder/LITTLE_ENDIAN) + :big-endian (.order retval ByteOrder/BIG_ENDIAN)) + retval)) diff --git a/src/tech/v3/datatype/mmap.clj b/src/tech/v3/datatype/mmap.clj index 3abb5d18..8ae10847 100644 --- a/src/tech/v3/datatype/mmap.clj +++ b/src/tech/v3/datatype/mmap.clj @@ -37,27 +37,24 @@ existing-fn (graal-native/if-defined-graal-native (errors/throwf "Automatic resolution of mmap pathways is disabled for graal native. Please use 'tech.3.datatype.mmap/set-mmap-impl!' prior to calling mmap-file.") - (let [jdk-19-fn - (when (>= (jdk-major-version) 19) - (try - (requiring-resolve 'tech.v3.datatype.mmap.mmodel-jdk19/mmap-file) - (catch Throwable _e - (log/warn "Failed to activate JDK-19 mmodel pathway; attempting jdk-17."))))] - (if jdk-19-fn - jdk-19-fn - (let [jdk-17-fn - (when (>= (jdk-major-version) 17) - (try - (requiring-resolve 'tech.v3.datatype.mmap.mmodel/mmap-file) - (catch Throwable _e - (log/warn "Failed to activate JDK-17 mmodel pathway; falling back to larray."))))] - (if jdk-17-fn - jdk-17-fn - (try - (requiring-resolve 'tech.v3.datatype.mmap.larray/mmap-file) - (catch Throwable e - (log/warnf "Mmap unavailble as larray failed to load: %s" e) - :failed))))))))))) + (let [jdk-version (jdk-major-version)] + (try + (requiring-resolve 'tech.v3.datatype.mmap.mmodel-jdk21/mmap-file) + (catch Throwable _e + (log/warn "Failed to activate JDK-21 mmodel pathway; attempting jdk-19.") + (try + (requiring-resolve 'tech.v3.datatype.mmap.mmodel-jdk19/mmap-file) + (catch Throwable _e + (log/warn "Failed to activate JDK-19 mmodel pathway; attempting jdk-17.") + (try + (requiring-resolve 'tech.v3.datatype.mmap.mmodel/mmap-file) + (catch Throwable _e + (log/warn "Failed to activate JDK-17 mmodel pathway; falling back to larray.") + (try + (requiring-resolve 'tech.v3.datatype.mmap.larray/mmap-file) + (catch Throwable e + (log/warnf "Mmap unavailble as larray failed to load: %s" e) + :failed)))))))))))))) (defn mmap-enabled? diff --git a/src/tech/v3/datatype/mmap/mmodel_jdk21.clj b/src/tech/v3/datatype/mmap/mmodel_jdk21.clj new file mode 100644 index 00000000..9ca57a0a --- /dev/null +++ b/src/tech/v3/datatype/mmap/mmodel_jdk21.clj @@ -0,0 +1,58 @@ +(ns tech.v3.datatype.mmap.mmodel-jdk21 + "Mmap based on the MemorySegment jdk21 memory model pathway" + (:require [tech.v3.datatype.ffi.native-buffer-mmodel-jdk21 :as nbuf-mmodel] + [tech.v3.resource :as resource]) + (:import [java.lang.foreign MemorySegment Arena] + [java.nio.channels FileChannel$MapMode FileChannel] + [java.nio.file Path Paths StandardOpenOption OpenOption] + [tech.v3.datatype.native_buffer NativeBuffer])) + + +(set! *warn-on-reflection* true) + + +(defn- ->path + ^Path [item] + (if (instance? Path item) + item + (Paths/get item (into-array String [])))) + + +(defn mmap-file + "Memory map a file returning a native buffer. fpath must resolve to a valid + java.io.File. + Options + * :resource-type - maps to tech.v3.resource `:track-type`, defaults to auto. + * :mmap-mode + * :read-only - default - map the data as shared read-only. + * :read-write - map the data as shared read-write. + * :map-len - Override the file's length to provide a mapping length" + (^NativeBuffer [fpath {:keys [resource-type mmap-mode] + :or {resource-type :auto + mmap-mode :read-only} + :as options}] + (let [flen (long (:map-len options (.length (java.io.File. (str fpath))))) + channel (FileChannel/open (->path fpath) + (into-array OpenOption + (case mmap-mode + :read-only + [StandardOpenOption/READ] + :read-write + [StandardOpenOption/READ + StandardOpenOption/WRITE] + :private + [StandardOpenOption/READ]))) + rscope (Arena/ofShared) + mseg (.map channel + (case mmap-mode + :read-only FileChannel$MapMode/READ_ONLY + :read-write FileChannel$MapMode/READ_WRITE + :private FileChannel$MapMode/PRIVATE) + 0 flen + rscope) + nbuf (nbuf-mmodel/memory-segment->native-buffer mseg options)] + (when resource-type + (resource/track nbuf {:track-type resource-type + :dispose-fn #(.close rscope)})) + nbuf)) + (^NativeBuffer [fpath] (mmap-file fpath {}))) diff --git a/src/tech/v3/datatype/native_buffer.clj b/src/tech/v3/datatype/native_buffer.clj index c9865322..dbbf8121 100644 --- a/src/tech/v3/datatype/native_buffer.clj +++ b/src/tech/v3/datatype/native_buffer.clj @@ -29,10 +29,6 @@ (set! *unchecked-math* :warn-on-boxed) -(graal-native/when-not-defined-graal-native - (require '[clojure.pprint :as pp])) - - (defn unsafe "Get access to an instance of sun.misc.Unsafe." ^Unsafe [] diff --git a/src/tech/v3/datatype/nio_buffer.clj b/src/tech/v3/datatype/nio_buffer.clj index faa424a7..1e0cc09d 100644 --- a/src/tech/v3/datatype/nio_buffer.clj +++ b/src/tech/v3/datatype/nio_buffer.clj @@ -114,17 +114,22 @@ (resource/chain-resources retval nbuf))) (do (try - (requiring-resolve 'tech.v3.datatype.ffi.nio-buf-mmodel-jdk19/direct-buffer-constructor) + (requiring-resolve 'tech.v3.datatype.ffi.nio-buf-mmodel-jdk21/direct-buffer-constructor) (catch Exception e (log/info "Unable to find direct buffer constructor - -falling back to jdk16 memory model.") +falling back to jdk19 memory model.") (try - (requiring-resolve 'tech.v3.datatype.ffi.nio-buf-mmodel/direct-buffer-constructor) + (requiring-resolve 'tech.v3.datatype.ffi.nio-buf-mmodel-jdk19/direct-buffer-constructor) (catch Exception e - (throw (RuntimeException. "Unable to load direct buffer constructor. If you are using JDK-17, set your runtime :jvm-opts as follows: + (log/info "Unable to find direct buffer constructor - +falling back to jdk16 memory model.") + (try + (requiring-resolve 'tech.v3.datatype.ffi.nio-buf-mmodel/direct-buffer-constructor) + (catch Exception e + (throw (RuntimeException. "Unable to load direct buffer constructor. If you are using JDK-17, set your runtime :jvm-opts as follows: :jvm-opts [\"--add-modules\" \"jdk.incubator.foreign,jdk.incubator.vector\" \"--enable-native-access=ALL-UNNAMED\"]}" - e)))))))))) + e)))))))))))) (defn native-buf->nio-buf