🔧 Add logs and fallbacks

This commit is contained in:
Elena Torro
2025-10-30 12:28:38 +01:00
parent 5690ea766f
commit e67d6bc7dc
2 changed files with 185 additions and 57 deletions

View File

@@ -14,6 +14,7 @@
[app.main.fonts :as fonts]
[app.main.store :as st]
[app.render-wasm.helpers :as h]
[app.render-wasm.mem :as mem]
[app.render-wasm.wasm :as wasm]
[app.util.http :as http]
[beicon.v2.core :as rx]
@@ -82,8 +83,20 @@
(let [font-id-buffer (:family-id-buffer font-data)
shape-id-buffer (uuid/get-u32 shape-id)
size (.-byteLength font-array-buffer)
ptr (h/call wasm/internal-module "_alloc_bytes" size)
heap (gobj/get ^js wasm/internal-module "HEAPU8")
ptr (h/call wasm/internal-module "_alloc_bytes" size)]
(if (or (nil? ptr) (zero? ptr))
;; Allocation failed - log error and return false
(do
(log/error :hint "Failed to allocate memory for font buffer"
:font-id (:font-id font-data)
:size size
:shape-id shape-id)
false)
;; Allocation succeeded - proceed with font storage
(try
(let [heap (gobj/get ^js wasm/internal-module "HEAPU8")
mem (js/Uint8Array. (.-buffer heap) ptr size)]
(.set mem (js/Uint8Array. font-array-buffer))
(h/call wasm/internal-module "_store_font"
@@ -106,7 +119,21 @@
(aget shape-id-buffer 2)
(aget shape-id-buffer 3))
true))
true)
(catch :default e
;; Error during font storage - free allocated memory and log error
(log/error :hint "Error storing font buffer"
:font-id (:font-id font-data)
:shape-id shape-id
:cause e)
(try
(h/call wasm/internal-module "_free_bytes" ptr size)
(catch :default free-error
(log/error :hint "Failed to free memory after font storage error"
:ptr ptr
:size size
:cause free-error)))
false)))))
(defn- fetch-font
[shape-id font-data font-url emoji? fallback?]
@@ -120,6 +147,7 @@
(log/error :hint "Could not fetch font"
:font-url font-url
:cause cause)
(mem/free)
(rx/empty))))})
(defn- google-font-ttf-url

View File

@@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.logging :as log]
[app.common.transit :as t]
[app.common.types.shape :as shape]
[app.common.types.shape.layout :as ctl]
@@ -21,12 +22,66 @@
(declare ^:private impl-conj)
(declare ^:private impl-dissoc)
;; Track pending async operations to prevent race conditions
(defonce ^:private pending-operations (atom {}))
(defn- cancel-pending-operations!
"Cancel all pending async operations for a given shape"
[shape-id]
(when-let [operations (get @pending-operations shape-id)]
(doseq [subscription operations]
(try
(rx/dispose! subscription)
(catch :default e
(log/error :hint "Error disposing subscription"
:shape-id shape-id
:cause e))))
(swap! pending-operations dissoc shape-id)))
(defn shape-in-current-page?
"Check if a shape is in the current page by looking up the current page objects"
[shape-id]
(let [objects (deref refs/workspace-page-objects)]
(contains? objects shape-id)))
(defn- add-pending-operation!
"Track a pending async operation for cleanup"
[shape-id subscription]
(swap! pending-operations update shape-id (fnil conj []) subscription)
subscription)
(defn- execute-with-safety-checks
"Execute callback only if shape is still valid, with proper error handling and memory validation"
[shape-id callback-fn]
(try
(when (shape-in-current-page? shape-id)
;; Validate that we can safely use the shape before proceeding
(api/use-shape shape-id)
;; Execute the callback with additional error protection
(try
(callback-fn)
(catch :default inner-e
(log/error :hint "Error in shape callback inner execution"
:shape-id shape-id
:cause inner-e)
;; Attempt to clean up any potential memory corruption
(try
(api/clear-drawing-cache)
(catch :default _cleanup-e
(log/warn :hint "Could not clear cache during error recovery"
:shape-id shape-id))))))
(catch :default e
(log/error :hint "Error in shape callback execution"
:shape-id shape-id
:cause e)
;; Cancel any remaining operations for this shape on critical error
(cancel-pending-operations! shape-id))))
(defn cleanup-shape-operations!
"Public function to cleanup all pending operations for a shape (e.g., when shape is deleted)"
[shape-id]
(cancel-pending-operations! shape-id))
(defn map-entry
[k v]
(cljs.core/MapEntry. k v nil))
@@ -231,47 +286,92 @@
(defn set-wasm-multi-attrs!
[shape properties]
(let [shape-id (dm/get-prop shape :id)]
(when (shape-in-current-page? shape-id)
(when (and shape-id (shape-in-current-page? shape-id))
;; Cancel any existing pending operations for this shape to prevent race conditions
(cancel-pending-operations! shape-id)
(try
(api/use-shape shape-id)
(let [result
(->> properties
(mapcat #(set-wasm-single-attr! shape %)))
pending (-> (d/index-by :key :callback result) vals)]
(if (and pending (seq pending))
(let [subscription
(->> (rx/from pending)
(rx/mapcat (fn [callback] (callback)))
(rx/reduce conj [])
(rx/subs!
(fn [_]
(execute-with-safety-checks shape-id
(fn []
(api/update-shape-tiles)
(api/clear-drawing-cache)
(api/request-render "set-wasm-attrs-pending"))))
(fn [error]
(log/error :hint "Error in async shape operation"
:shape-id shape-id
:cause error))))]
;; Cancel any existing pending operations for this shape
(cancel-pending-operations! shape-id)
;; Track the new operation
(add-pending-operation! shape-id subscription))
(do
(api/update-shape-tiles)
(api/request-render "set-wasm-attrs")))))))
(api/request-render "set-wasm-attrs"))))
(catch :default e
(log/error :hint "Error in set-wasm-multi-attrs!"
:shape-id shape-id
:properties properties
:cause e)
;; Clean up on error
(cancel-pending-operations! shape-id))))))
(defn set-wasm-attrs!
[shape k v]
(let [shape-id (dm/get-prop shape :id)
old-value (get shape k)]
(when (and (shape-in-current-page? shape-id)
(when (and shape-id
(shape-in-current-page? shape-id)
(not (identical? old-value v)))
;; Cancel any existing pending operations for this shape to prevent race conditions
(cancel-pending-operations! shape-id)
(let [shape (assoc shape k v)]
(try
(api/use-shape shape-id)
(let [result (set-wasm-single-attr! shape k)
pending (-> (d/index-by :key :callback result) vals)]
(if (and pending (seq pending))
(let [subscription
(->> (rx/from pending)
(rx/mapcat (fn [callback] (callback)))
(rx/reduce conj [])
(rx/subs!
(fn [_]
(execute-with-safety-checks shape-id
(fn []
(api/update-shape-tiles)
(api/clear-drawing-cache)
(api/request-render "set-wasm-attrs-pending"))))
(fn [error]
(log/error :hint "Error in async shape operation"
:shape-id shape-id
:cause error))))]
;; Cancel any existing pending operations for this shape
(cancel-pending-operations! shape-id)
;; Track the new operation
(add-pending-operation! shape-id subscription))
(do
(api/update-shape-tiles)
(api/request-render "set-wasm-attrs"))))))))
(api/request-render "set-wasm-attrs"))))
(catch :default e
(log/error :hint "Error in set-wasm-attrs!"
:shape-id shape-id
:attribute k
:cause e)
;; Clean up on error
(cancel-pending-operations! shape-id)))))))
(defn- impl-assoc
[self k v]