Skip to content

Commit ead23d6

Browse files
authored
Fixed bug where interrupt state monitoring could get out of sync if interrupts started flooding in during startup. (#2427)
(cherry picked from commit 11a84419fc77a38e6ce07f335beb6c2b9ae56618)
1 parent 8b6c1d5 commit ead23d6

File tree

1 file changed

+23
-21
lines changed

1 file changed

+23
-21
lines changed

src/devices/Tca955x/Tca955x.cs

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ public abstract class Tca955x : GpioDriver
2020
private readonly int _interrupt;
2121
private readonly Dictionary<int, PinValue> _pinValues = new Dictionary<int, PinValue>();
2222
private readonly ConcurrentDictionary<int, PinChangeEventHandler> _eventHandlers = new ConcurrentDictionary<int, PinChangeEventHandler>();
23-
private readonly Dictionary<int, PinEventTypes> _interruptPins = new Dictionary<int, PinEventTypes>();
24-
private Dictionary<int, PinValue> _interruptLastInputValues = new Dictionary<int, PinValue>();
23+
private readonly Dictionary<int, PinEventTypes> _interruptPinsSubscribedEvents = new Dictionary<int, PinEventTypes>();
24+
private readonly ConcurrentDictionary<int, PinValue> _interruptLastInputValues = new ConcurrentDictionary<int, PinValue>();
2525

2626
private GpioController? _controller;
2727

@@ -89,7 +89,15 @@ protected Tca955x(I2cDevice device, int interrupt = -1, GpioController? gpioCont
8989
}
9090

9191
if (_interrupt != -1)
92-
{
92+
{
93+
// Initialise the interrupt handling state because ints may start coming from the INT pin
94+
// on the expander as soon as we register the interrupt handler.
95+
for (int i = 0; i < PinCount; i++)
96+
{
97+
_interruptPinsSubscribedEvents.Add(i, PinEventTypes.None);
98+
_interruptLastInputValues.TryAdd(i, PinValue.Low);
99+
}
100+
93101
_shouldDispose = shouldDispose || gpioController is null;
94102
_controller = gpioController ?? new GpioController();
95103
if (!_controller.IsPinOpen(_interrupt))
@@ -456,7 +464,7 @@ private Task ProcessInterruptInTask()
456464
{
457465
// Take a snapshot of the current interrupt pin configuration and last known input values
458466
// so we can safely process them outside the lock in a background task.
459-
var interruptPinsSnapshot = new Dictionary<int, PinEventTypes>(_interruptPins);
467+
var interruptPinsSnapshot = new Dictionary<int, PinEventTypes>(_interruptPinsSubscribedEvents);
460468
var interruptLastInputValuesSnapshot = new Dictionary<int, PinValue>(_interruptLastInputValues);
461469

462470
Task processingTask = new Task(() =>
@@ -494,9 +502,9 @@ private Task ProcessInterruptInTask()
494502
interruptLastInputValuesSnapshot[pin] = newValue;
495503
}
496504

497-
lock (_interruptHandlerLock)
498-
{
499-
_interruptLastInputValues = interruptLastInputValuesSnapshot;
505+
foreach (var pin in interruptLastInputValuesSnapshot.Keys)
506+
{
507+
_interruptLastInputValues.TryUpdate(pin, interruptLastInputValuesSnapshot[pin], !interruptLastInputValuesSnapshot[pin]);
500508
}
501509
}
502510
});
@@ -557,16 +565,13 @@ protected override void AddCallbackForPinValueChangedEvent(int pinNumber, PinEve
557565

558566
lock (_interruptHandlerLock)
559567
{
560-
if (_interruptPins.ContainsKey(pinNumber))
561-
{
562-
throw new InvalidOperationException($"A callback is already registered for pin {pinNumber}");
563-
}
564-
else
568+
_interruptPinsSubscribedEvents[pinNumber] = eventType;
569+
var currentValue = Read(pinNumber);
570+
_interruptLastInputValues.TryUpdate(pinNumber, currentValue, !currentValue);
571+
if (!_eventHandlers.TryAdd(pinNumber, callback))
565572
{
566-
_interruptPins.Add(pinNumber, eventType);
567-
_interruptLastInputValues.Add(pinNumber, Read(pinNumber));
568-
_eventHandlers[pinNumber] = callback;
569-
}
573+
throw new InvalidOperationException($"An event handler is already registered for pin {pinNumber}");
574+
}
570575
}
571576
}
572577

@@ -575,11 +580,8 @@ protected override void RemoveCallbackForPinValueChangedEvent(int pinNumber, Pin
575580
{
576581
lock (_interruptHandlerLock)
577582
{
578-
if (_eventHandlers.TryRemove(pinNumber, out _))
579-
{
580-
_interruptPins.Remove(pinNumber);
581-
_interruptLastInputValues.Remove(pinNumber);
582-
}
583+
_eventHandlers.TryRemove(pinNumber, out _);
584+
_interruptPinsSubscribedEvents[pinNumber] = PinEventTypes.None;
583585
}
584586
}
585587

0 commit comments

Comments
 (0)