jbonofre commented on code in PR #739:
URL: https://github.com/apache/camel-karaf/pull/739#discussion_r3851027145


##########
core/camel-core-osgi/src/main/java/org/apache/camel/karaf/core/OsgiTypeConverter.java:
##########
@@ -242,15 +251,26 @@ public void setTypeConverterExists(TypeConverterExists 
typeConverterExists) {
     }
 
     public DefaultTypeConverter getDelegate() {
-        if (delegate == null) {
-            // ensure the tracker is open so we can discover 
TypeConverterLoader services
-            // before creating the registry - this is important because 
getDelegate() may be
-            // called during doInit() (e.g. when to() eagerly creates 
endpoints) which happens
-            // before doStart() where the tracker is normally opened
-            ensureTrackerOpen();
-            delegate = createRegistry();
+        DefaultTypeConverter answer = delegate;
+        if (answer == null) {
+            // double checked locking against the volatile field: getDelegate 
is on the conversion hot path,
+            // so the common case must stay lock free, but the check and the 
assignment together are not
+            // atomic - without the lock two threads racing on first access 
each build a registry, and
+            // whatever was registered on the one that loses is silently 
dropped
+            synchronized (this) {
+                answer = delegate;
+                if (answer == null) {
+                    // ensure the tracker is open so we can discover 
TypeConverterLoader services
+                    // before creating the registry - this is important 
because getDelegate() may be
+                    // called during doInit() (e.g. when to() eagerly creates 
endpoints) which happens
+                    // before doStart() where the tracker is normally opened
+                    ensureTrackerOpen();
+                    answer = createRegistry();

Review Comment:
   As mentioned before:
   
   `createRegistry()` is called here while holding `synchronized(this)`. Inside 
`createRegistry()`, it calls `tracker.getServiceReferences()` and 
`tracker.getService()`, which the Felix/Equinox `ServiceTracker` guards with 
its own internal `Tracked` monitor.
   
   Meanwhile, the OSGi framework holds that same `Tracked` monitor when 
dispatching service events — it calls `addingService()` / `removedService()`, 
which this PR has made `synchronized(this)`.
   
   Thread A: holds `synchronized(this)` → calls `tracker.getService()` → waits 
for `Tracked` monitor  
   Thread B: holds `Tracked` monitor → calls `addingService()` → waits for 
`synchronized(this)`
   
   Classic ABBA deadlock. This lock order did not exist before this PR. Fix: 
snapshot the tracked services *outside* any lock (e.g. call 
`tracker.getTracked()` which returns a defensive copy, or move the `tracker.*` 
calls before entering `synchronized(this)`), then use the snapshot inside the 
lock.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to