When moving 2 joysticks on 2 different input devices mapped to 1 XOutput device at the same time, occasionally one of the joystick inputs would be lost/disconnect.
In game, this caused the joystick to remain pressed in some last-polled direction.
This could be resolved in the XOutput app by hitting the "Force Refresh" button.
Debug/conclusion process:
Log would show a "WARN Poll failed for ..." when this occurs.
Log event is from DirectDevice.cs line 319 (logger.Warning($"Poll failed for {ToString()}");)
The exception thrown to cause the log event was a "Collection was modified ..."
Exception was thrown from line 313 (InputChanged?.Invoke(this, deviceInputChangedEventArgs);)
...which was invoking XOutputDevice.SourceInputChange() which then calls RefreshInput()
Debugging calls in the function, it appeared to be the call to changes.Any() that was throwing the exception.
Looking online for other instances of when such an exception could occur, the quick solution is to make a copy of the Collection for iteration. Adding ToList() to line 119 (changes.GetChanges(force);) appeared to fix the issue, but is more masking the problem than fixing it.
How is the Collection being changed? Each DirectDevice object that eventually calls into XOutputDevice.RefreshInput() has a thread. RefreshInput() makes changes to the state variable of XOutputDevice. It is possible for more than one thread to be calling RefreshInput() and thus making asynchronous changes to the state variable.
Excluding it's initialization, state is only used/of-use in RefreshInput().
Solution: Make state a local variable of RefreshInput().
This does mean a DeviceState is instantiated for each call to RefreshInput() which is inefficient.
Originally observed issue:
Debug/conclusion process:
logger.Warning($"Poll failed for {ToString()}");
)InputChanged?.Invoke(this, deviceInputChangedEventArgs);
)XOutputDevice.SourceInputChange()
which then callsRefreshInput()
changes.Any()
that was throwing the exception.ToList()
to line 119 (changes.GetChanges(force);
) appeared to fix the issue, but is more masking the problem than fixing it.DirectDevice
object that eventually calls intoXOutputDevice.RefreshInput()
has a thread.RefreshInput()
makes changes to thestate
variable ofXOutputDevice
. It is possible for more than one thread to be callingRefreshInput()
and thus making asynchronous changes to thestate
variable.state
is only used/of-use inRefreshInput()
.state
a local variable ofRefreshInput()
.This does mean a
DeviceState
is instantiated for each call toRefreshInput()
which is inefficient.