{"id":18610455,"url":"https://github.com/neilotoole/fifomu","last_synced_at":"2025-04-10T22:32:10.960Z","repository":{"id":218012222,"uuid":"745366963","full_name":"neilotoole/fifomu","owner":"neilotoole","description":"Mutex with FIFO lock acquisition","archived":false,"fork":false,"pushed_at":"2024-04-08T13:14:26.000Z","size":38,"stargazers_count":11,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-25T05:41:58.391Z","etag":null,"topics":["acquisition","fifo","go","golang","lock","locking","mutex","queue","sync"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/neilotoole.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-19T07:18:55.000Z","updated_at":"2024-07-31T03:13:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"a78d9777-1fbd-48ad-9f82-9c9bdd914f22","html_url":"https://github.com/neilotoole/fifomu","commit_stats":null,"previous_names":["neilotoole/fifomu"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neilotoole%2Ffifomu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neilotoole%2Ffifomu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neilotoole%2Ffifomu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neilotoole%2Ffifomu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/neilotoole","download_url":"https://codeload.github.com/neilotoole/fifomu/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248310052,"owners_count":21082337,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acquisition","fifo","go","golang","lock","locking","mutex","queue","sync"],"created_at":"2024-11-07T03:10:04.646Z","updated_at":"2025-04-10T22:32:10.638Z","avatar_url":"https://github.com/neilotoole.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Go Reference](https://pkg.go.dev/badge/github.com/neilotoole/fifomu.svg)](https://pkg.go.dev/github.com/neilotoole/fifomu)\n[![Go Report Card](https://goreportcard.com/badge/neilotoole/fifomu)](https://goreportcard.com/report/neilotoole/fifomu)\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/neilotoole/fifomu/blob/master/LICENSE)\n![Pipeline](https://github.com/neilotoole/fifomu/actions/workflows/go.yml/badge.svg)\n\n# fifomu: mutex with FIFO lock acquisition\n\n[`fifomu`](https://pkg.go.dev/github.com/neilotoole/fifomu) is a Go\npackage that provides a [`Mutex`](https://pkg.go.dev/github.com/neilotoole/fifomu#Mutex)\nwhose [`Lock`](https://pkg.go.dev/github.com/neilotoole/fifomu#Mutex.Lock) method returns\nthe lock to callers in FIFO call order. This is in contrast to [`sync.Mutex`](https://pkg.go.dev/sync#Mutex),\nwhere a single goroutine can repeatedly lock and unlock and relock the mutex\nwithout handing off to other lock waiter goroutines (that is, until after a 1ms\nstarvation threshold, at which point `sync.Mutex` enters a FIFO \"starvation mode\"\nfor those starved waiters, but that's too late for some use cases).\n\n`fifomu.Mutex` implements the exported methods of `sync.Mutex` and thus is\na drop-in replacement (and by extension, also implements [`sync.Locker`](https://pkg.go.dev/sync#Locker)).\nIt also provides a bonus context-aware [`LockContext`](https://pkg.go.dev/github.com/neilotoole/fifomu#Mutex.LockContext)\nmethod.\n\nNote: unless you need the FIFO behavior, you should prefer `sync.Mutex`.\nFor typical workloads, its \"greedy-relock\" behavior requires less goroutine\nswitching and yields better performance. See the [benchmarks](#benchmarks)\nsection below.\n\n\n## Usage\n\nAdd the package to your `go.mod` via `go get`:\n\n```shell\ngo get github.com/neilotoole/fifomu\n```\n\nThen import it and use it as you would `sync.Mutex`:\n\n```go\npackage main\n\nimport \"github.com/neilotoole/fifomu\"\n\nfunc main() {\n  mu := \u0026fifomu.Mutex{}\n  mu.Lock()\n  defer mu.Unlock()\n  \n  // ... Do something critical\n}\n```\n\nAdditionally, `fifomu` provides a context-aware\n[`Mutex.LockContext`](https://pkg.go.dev/github.com/neilotoole/fifomu#Mutex.LockContext)\nmethod that blocks until the mutex is available or ctx is done, returning the\nvalue of [`context.Cause(ctx)`](https://pkg.go.dev/context#Cause).\n\n```go\nfunc foo(ctx context.Context) error {\n  mu := \u0026fifomu.Mutex{}\n  if err := mu.LockContext(ctx); err != nil {\n    // Oh dear, ctx was cancelled\n    return err\n  }\n  defer mu.Unlock()\n  \n  // ... Do something critical\n  \n  return nil\n}\n```\n\n## Benchmarks\n\nThe benchmark results below were obtained on a 2021 MacBook Pro (M1 Max)\nusing Go 1.21.6.\n\nThe benchmarks compare three mutex implementations:\n\n- `stdlib` is `sync.Mutex`\n- `fifomu` is `fifomu.Mutex` (this package)\n- `semaphoreMu` is a trivial mutex implementation built on top of \n [`semaphore.Weighted`](https://pkg.go.dev/golang.org/x/sync/semaphore); it\n exists only in the test code, as a comparison baseline.\n\nWhat conclusions can be drawn from the results below? Well, `fifomu`\nis always at least 1.5x slower than `sync.Mutex`, and often more. The\nworst results are in `BenchmarkMutexSpin`, where `fifomu` is 10x slower\nthan `sync.Mutex`. On the plus side, `fifomu` is always faster than\nthe baseline `semaphoreMu` implementation, and unlike that baseline,\ncalls to `fifomu`'s `Mutex.Lock` method do not allocate.\n\nBenchmark your own workload before committing to `fifomu.Mutex`. In many\ncases you will be able to design around the need for FIFO lock acquisition.\nIt's a bit of a code smell to begin with, but, that said, sometimes it's the\nmost straightforward solution.\n\n```\n$ GOMAXPROCS=10 go test -bench .\ngoos: darwin\ngoarch: arm64\npkg: github.com/neilotoole/fifomu\nBenchmarkMutexUncontended/stdlib-10             738603763              1.654 ns/op             0 B/op          0 allocs/op\nBenchmarkMutexUncontended/fifomu-10             361784637              3.266 ns/op             0 B/op          0 allocs/op\nBenchmarkMutexUncontended/semaphoreMu-10        344456692              3.298 ns/op             0 B/op          0 allocs/op\nBenchmarkMutex/stdlib-10                        10600124               110.2 ns/op             0 B/op          0 allocs/op\nBenchmarkMutex/fifomu-10                         7731985               153.9 ns/op             0 B/op          0 allocs/op\nBenchmarkMutex/semaphoreMu-10                    5520886               217.4 ns/op           159 B/op          2 allocs/op\nBenchmarkMutexSlack/stdlib-10                   11174293               109.4 ns/op             0 B/op          0 allocs/op\nBenchmarkMutexSlack/fifomu-10                    6911655               164.0 ns/op             0 B/op          0 allocs/op\nBenchmarkMutexSlack/semaphoreMu-10               5330490               227.0 ns/op           159 B/op          2 allocs/op\nBenchmarkMutexWork/stdlib-10                    10140280               120.0 ns/op             0 B/op          0 allocs/op\nBenchmarkMutexWork/fifomu-10                     6797134               176.5 ns/op             0 B/op          0 allocs/op\nBenchmarkMutexWork/semaphoreMu-10                4979712               240.6 ns/op           159 B/op          2 allocs/op\nBenchmarkMutexWorkSlack/stdlib-10               10004154               119.4 ns/op             0 B/op          0 allocs/op\nBenchmarkMutexWorkSlack/fifomu-10                6362150               188.7 ns/op             0 B/op          0 allocs/op\nBenchmarkMutexWorkSlack/semaphoreMu-10           4678826               256.8 ns/op           160 B/op          3 allocs/op\nBenchmarkMutexNoSpin/stdlib-10                   9500152               131.2 ns/op            12 B/op          0 allocs/op\nBenchmarkMutexNoSpin/fifomu-10                   3132691               381.3 ns/op            12 B/op          0 allocs/op\nBenchmarkMutexNoSpin/semaphoreMu-10              2502936               461.0 ns/op            51 B/op          1 allocs/op\nBenchmarkMutexSpin/stdlib-10                     5025486               233.7 ns/op             0 B/op          0 allocs/op\nBenchmarkMutexSpin/fifomu-10                      514082               2362 ns/op              0 B/op          0 allocs/op\nBenchmarkMutexSpin/semaphoreMu-10                 496808               2512 ns/op            159 B/op          2 allocs/op\n```\n\n## Related\n\n- [`streamcache`](https://github.com/neilotoole/streamcache) uses `fifomu` to ensure fairness\n  for concurrent readers of a stream.\n- [`sq`](https://github.com/neilotoole/sq) uses `fifomu` indirectly via `streamcache`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneilotoole%2Ffifomu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneilotoole%2Ffifomu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneilotoole%2Ffifomu/lists"}