Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

LibGpiodV2EventObserver never disposes edge events: one finalizable SafeHandle per edge causes multi-ms gen0 GC pauses

Open Beginner friendly
#2,626 1 comment 1 reaction 2 assignees View on GitHub

Maintainers usually reply within 1 day

@krwq is already working on this.

Since Oct 22, 2026.

  • #2629 by @copilot-swe-agent — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
csharp

Research direction

Start in LibGpiodV2EventObserver.HandleEdgeEventsOfRequestInLoop and inspect how EdgeEventBuffer.GetEvent creates the event handle. Ensure the event is disposed after its callbacks run, then check the event observer's tests or reproduce with the GPIO setup described in the issue. Done means each edge event is disposed promptly and the finalizer queue no longer grows by one handle per edge.

Written by the indexing model from the issue text.

Description

untriaged
Description

With LibGpiodV2Driver (also used by RaspberryPi3Driver for interrupts), every edge event leaves an undisposed SafeHandle behind for the finalizer. EdgeEventBuffer.GetEvent returns a copy (gpiod_edge_event_copy, wrapped in an EdgeEventSafeHandle), and LibGpiodV2EventObserver.HandleEdgeEventsOfRequestInLoop never disposes it:

EdgeEvent edgeEvent = edgeEventBuffer.GetEvent((ulong)i);
HandleEdgeEvent(edgeEvent);

Every gen0 GC therefore has to promote and queue one finalizable object per edge received since the previous GC.

Repro
using var gpio = new GpioController();
gpio.OpenPin(23, PinMode.Output, PinValue.Low);
gpio.OpenPin(15, PinMode.InputPullUp);
PinValue state = PinValue.Low;
gpio.RegisterCallbackForPinValueChangedEvent(15, PinEventTypes.Falling, (_, _) => gpio.Write(23, state = !state));
Thread.Sleep(Timeout.Infinite);

1 kHz square wave on GPIO15, logic analyzer on GPIO15 and GPIO23.

Measured

Raspberry Pi CM4, .NET 10.0.13, System.Device.Gpio 4.2.0, libgpiod 2.2.1, Linux 6.18 PREEMPT_RT.

  • Response is typically 31 µs, but every 2.25 s the process stalls for 3 ms and 3 edges are missed.
  • EventPipe (GC keyword): a gen0 GC every 2.25 s with ~2250 finalization-promoted objects, one per edge.
  • Pause 3.0 ms = suspend 45 µs + GC work 2.7 ms + restart 15 µs. The finalizer thread then runs for 1.2 ms.
  • The pause grows with the number of edges since the previous GC: about 330 µs + 1.1 µs per edge. DOTNET_GCgen0size=0x10000 gives ~410 µs every 70 ms, 0x40000 gives ~740 µs every 280 ms.

In total about 1 KB is allocated per edge. WaitEdgeEventsRespectfully also creates an epoll fd, a pipe and an UnmanagedArray on every wait.

Suggested fix

Dispose the event after the callbacks have run:

using EdgeEvent edgeEvent = edgeEventBuffer.GetEvent((ulong)i);

A further step would be to read the event fields straight from the buffer instead of copying the event, which makes the event path allocation-free.

Dominant language
C#
Stars
2.4k
Forks
630
Avg merge
6d 17h
Merged PRs (30d)
10

Getting set up

  • No Dockerfile or Docker Compose file
  • Has a pull request template
  • No contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dotnet/iot

All issues in dotnet/iot

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.