-
-
Notifications
You must be signed in to change notification settings - Fork 29
Description
Summary
AmpliPi is leaking I2C file descriptors, causing the application to crash with OSError: [Errno 24] Too many open files after running for several hours.
Environment
AmpliPi Version: 0.4.8
Hardware: Raspberry Pi
Python Version: 3.7
OS: Raspbian/Raspberry Pi OS
Problem Description
The AmpliPi service crashes repeatedly with the following error:
textNov 07 20:43:02 amplipi authbind[851]: OSError: [Errno 24] Too many open files
Nov 07 20:43:02 amplipi authbind[851]: ERROR:asyncio:socket.accept() out of system resource
Root Cause Analysis
Investigation reveals that the application is leaking file descriptors to /dev/i2c-1. The process accumulates over 1000 open file handles to the I2C device, eventually hitting the system limit of 1024.
Diagnostic Output:
bash$ lsof -p 851 | grep CHR | awk '{print $NF}' | sort | uniq -c | sort -rn
1014 /dev/i2c-1
2 /dev/gpiomem
1 /dev/null
bash$ cat /proc/851/limits | grep "open files"
Max open files 1024 1048576 files
bash$ ls -l /proc/851/fd | wc -l
1025
The process has 1025 open file descriptors with 1014 of them being handles to /dev/i2c-1.
Expected vs Actual Behavior
Expected: The I2C bus should be opened once and reused, OR file handles should be properly closed after each I2C operation using context managers.
Actual: Each I2C operation opens a new file handle to /dev/i2c-1 that is never closed, causing file descriptor exhaustion over time.
Likely Code Issue
The problem is likely in code that accesses I2C devices (preamps, audio chips, etc.). The code probably looks something like this:
:x: Bad (current):
pythonbus = smbus.SMBus(1) # Opens /dev/i2c-1
bus.write_byte_data(addr, reg, value)
Never closes the file descriptor!
:white_check_mark: Good (should be):
python# Option 1: Use context manager
with smbus.SMBus(1) as bus:
bus.write_byte_data(addr, reg, value)
Automatically closes
# Option 2: Open once, reuse everywhere
class AudioController:
def __init__(self):
self.bus = smbus.SMBus(1) # Open once
def set_volume(self, addr, value):
self.bus.write_byte_data(addr, reg, value) # Reuse
def __del__(self):
self.bus.close() # Clean up
Workaround (Temporary Fix)
Increase the file descriptor limit to prevent immediate crashes:
bashsystemctl --user edit amplipi.service --full
Add under the [Service] section:
iniLimitNOFILE=8192
Then reload and restart:
bashsystemctl --user daemon-reload
systemctl --user restart amplipi.service
Suggested Fix for Developers
Search the codebase for all smbus.SMBus() or similar I2C access calls
Choose one approach:
Use context managers (with statements) to ensure handles are closed
Open the I2C bus once at application startup and reuse the same handle
Explicitly call .close() on I2C handles when done
Consider using a singleton pattern or dependency injection to share a single I2C bus instance across the application
Additional Notes
This issue will affect any AmpliPi installation running continuously. The time to failure depends on how frequently I2C operations occur (volume changes, input switching, preamp adjustments, etc.).
Happy to provide additional diagnostic information or logs if needed!