Fix deadlock in spatial_scene — lock re-entrancy
observe() held self._lock, called _check_anomaly, which called get_usual_direction, which tried to acquire self._lock again → deadlock. Split into _usual_direction_unlocked (no lock) for internal use. This caused /scene and all other API endpoints to hang after the first sound classification with spatial data. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -168,8 +168,8 @@ class SpatialScene:
|
||||
if now - self._last_anomaly.get(category, 0) < 30.0:
|
||||
return None
|
||||
|
||||
# Find the usual direction for this category
|
||||
usual_angle = self.get_usual_direction(category)
|
||||
# Find the usual direction for this category (already holding lock)
|
||||
usual_angle = self._usual_direction_unlocked(category)
|
||||
if usual_angle is None:
|
||||
return None
|
||||
|
||||
@@ -192,34 +192,36 @@ class SpatialScene:
|
||||
|
||||
return None
|
||||
|
||||
def _usual_direction_unlocked(self, category: str) -> Optional[float]:
|
||||
"""Get the most common direction for a category. Caller must hold self._lock."""
|
||||
bins = self.scene_map.get(category)
|
||||
if not bins:
|
||||
return None
|
||||
|
||||
total_weight = sum(bins.values())
|
||||
if total_weight == 0:
|
||||
return None
|
||||
|
||||
sin_sum = 0.0
|
||||
cos_sum = 0.0
|
||||
for bin_idx, count in bins.items():
|
||||
angle_rad = math.radians(_bin_center(bin_idx))
|
||||
sin_sum += count * math.sin(angle_rad)
|
||||
cos_sum += count * math.cos(angle_rad)
|
||||
|
||||
return math.degrees(math.atan2(sin_sum, cos_sum)) % 360
|
||||
|
||||
def get_usual_direction(self, category: str) -> Optional[float]:
|
||||
"""Get the most common direction for a sound category (weighted average)."""
|
||||
"""Get the most common direction for a sound category (thread-safe)."""
|
||||
with self._lock:
|
||||
bins = self.scene_map.get(category)
|
||||
if not bins:
|
||||
return None
|
||||
|
||||
# Weighted circular mean
|
||||
total_weight = sum(bins.values())
|
||||
if total_weight == 0:
|
||||
return None
|
||||
|
||||
sin_sum = 0.0
|
||||
cos_sum = 0.0
|
||||
for bin_idx, count in bins.items():
|
||||
angle_rad = math.radians(_bin_center(bin_idx))
|
||||
sin_sum += count * math.sin(angle_rad)
|
||||
cos_sum += count * math.cos(angle_rad)
|
||||
|
||||
mean_angle = math.degrees(math.atan2(sin_sum, cos_sum)) % 360
|
||||
return mean_angle
|
||||
return self._usual_direction_unlocked(category)
|
||||
|
||||
def get_scene_summary(self) -> dict:
|
||||
"""Get a summary of the learned spatial scene."""
|
||||
with self._lock:
|
||||
summary = {}
|
||||
for category in sorted(self.scene_map.keys()):
|
||||
usual = self.get_usual_direction(category)
|
||||
usual = self._usual_direction_unlocked(category)
|
||||
total = self.category_totals[category]
|
||||
if usual is not None:
|
||||
summary[category] = {
|
||||
|
||||
Reference in New Issue
Block a user