Skip to content

lock: add per-bridge advisory locking - #89

Closed
JacksonBopp wants to merge 1 commit into
amboar:mainfrom
JacksonBopp:bridge-locking
Closed

JacksonBopp wants to merge 1 commit into
amboar:mainfrom
JacksonBopp:bridge-locking

Conversation

@JacksonBopp

Copy link
Copy Markdown

Summary

Implements the bridge-operations layer of the locking design you laid out in #48: a flock(2)-based advisory lock scoped to each bridge driver's name (/run/lock/culvert.${bridgename}), held across both the probe attempt and the bridge's subsequent use, so two concurrent culvert instances can't race on the same hardware.

  • Lock is acquired in host_probe_bridge() before driver->probe() runs, since probing itself drives the bridge (e.g. fingerprinting) and needs the same protection as later use.
  • Released on a failed probe or allocation failure; otherwise held on struct bridge until host_destroy() releases it alongside the driver's own destroy().
  • Per your follow-up comment on Prevent Multiple Culvert Instances From Running #48, the fingerprinting-layer lock is intentionally left out — those operations are reads only.
  • The SoC-level protection-bit layer (SCU40[1] on 2400/2500, SCU100[1] on 2600) is a separate, chip-generation-dependent piece of work and isn't part of this PR.

Test plan

No hardware bridges available in my environment, so verification is at the locking-primitive level rather than end-to-end against real bridge drivers:

  • Builds cleanly via the project's meson/ninja flow (meson setup + ninja), no warnings
  • clang-format --dry-run --Werror clean against the project's .clang-format
  • Standalone test: second lock_acquire() on the same name correctly fails with EWOULDBLOCK; a different name succeeds concurrently; re-acquiring after lock_release() succeeds
  • Standalone test: real two-process race via fork() — child is correctly denied the lock while the parent holds it, confirming the mechanism works across processes, not just within one

Fixes #48

🤖 Generated with Claude Code

Add a flock(2)-based advisory lock scoped to each bridge driver's
name, held for the duration of a probe attempt and the bridge's
subsequent use, so two concurrent culvert instances can't race on
the same underlying hardware.

The lock is acquired in host_probe_bridge() before driver->probe()
runs, since probing itself can drive the bridge (e.g. to fingerprint
what's present) and is exactly the kind of access that must not
race. It's released on a failed probe or allocation failure, and
otherwise held on the struct bridge until host_destroy() tears it
down alongside the driver's own destroy() call.

This implements the bridge-operations layer of the three-layer
locking design amboar laid out in amboar#48. Per a follow-up comment on
that issue, the fingerprinting layer is intentionally left
unimplemented, since those operations are reads and a separate lock
there wasn't judged to buy much. The SoC-level protection-bit layer
(SCU40[1] on 2400/2500, SCU100[1] on 2600) is a distinct,
chip-generation-dependent feature and is left as future work.

Fixes: amboar#48
Signed-off-by: Jackson Bopp <boppjackson@gmail.com>
@amboar

amboar commented Sep 10, 2026 •

Copy link
Copy Markdown
Owner

Hi, I have a few thoughts here.

Ultimately, culvert is a tool for expert use. It tends to have sufficient capabilities to help those experts extract themselves from silly jams with supported SoCs, though its also expected that may require some hacking on their part. Contributions based on work done on the tool in the process of extracting yourself from a jam tend to be the most valuable. That is necessarily done by people who have a need for what the project already provides.

A resulting property is that culvert will never really be in a polished state. It will always be a box of parts that were sufficient to achieve some other goal. It's nice if the parts happen to be useful to others, but they must at least have been useful to the contributor.

All the existing contributors to culvert have their wits enough about themselves to implement the locking. The approach and its implementation shouldn't be novel - it's more a case of prioritising some drudgery to achieve that other goal.

Unfortunately it's hard to consider you as someone who has a need for the project if you don't have access to the hardware it supports. I don't intend to accept contributions to the project where the contribution itself is the goal, and less so when the work is machine-generated and unlikely to benefit your learning.

What I find satisfying, as a maintainer of the project, is seeing the growth in collective understanding and capability that emerges from whatever crisis motivated the work leading to the contribution1. I don't enjoy reviewing code produced by the current generation of agents. I don't enjoy the time-asymmetry of personally reviewing code that other people haven't themselves written. I don't enjoy the feeling of the lack of personal learning involved in passing the feedback I create back into an agent to generate some hopefully acceptable change. I also don't intend to have the contribution process descend into countering production of code with agents by reviewing code with agents, as that gives the impression that any of the lack of personal understanding (now on both parts) is acceptable.

So more broadly than this PR, my position is that we don't accept agent-generated contributions.

While your personal development processes are not for me to chose, I ask that contributors are accountable for the proposed changes in their entirety, in the understanding of the problem and the implementation of the solution, to the exclusion of needing to credit agents. The need to credit an agent with the work is an indication that your understanding and effort hasn't met that bar.

So, thanks for the PR, however, I don't intend to accept or review it at this time, on the basis of the concerns outlined above.

Footnotes

  1. That is not to say I enjoy other peoples' crises. ↩

@JacksonBopp

Copy link
Copy Markdown
Author

I think you made really good points, and I appreciate you replying and giving me good advice. I'll keep these in mind, and I apologize for thoughtlessly attempting to contribute. Thank you, and sorry again. Next time you see a commit from me, it will be my own code.

@amboar

amboar commented Sep 10, 2026

Copy link
Copy Markdown
Owner

@JacksonBopp

I apologize for thoughtlessly attempting to contribute. Thank you, and sorry again.

I don't think there's any need for you to apologise! I'm not aware that that it was thoughtless - you at least managed to dig out something to improve based on past discussions that you weren't otherwise involved in. I also hadn't written these thoughts down anywhere else (despite stewing on them for some time for reasons unrelated to your PR), so there's no way you could have known prior to doing as you've done.

So, thanks for the interest, and if you do encounter circumstances where culvert is helpful to you, then I will be keen to work with your contributions.

@JacksonBopp

JacksonBopp commented Sep 10, 2026 via email

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prevent Multiple Culvert Instances From Running

2 participants