detached f567416

Task 5: goroutine-safe Register, preserve loop result, remove placeholder

- internal/capi/register.go: Use sync.Once for goroutine-safe initialization

- pipewire/interfaces.go: MainLoop.Run() now returns int32

- pipewire/runtime.go: wrapper returns inner.Run() result

- pipewire/loop.go: Removed empty placeholder file
commit
f5674163744591ed220f2712c3b096d8ea529b7f
author
brice · · 2026-04-09 06:29 UTC
parent
7d5e644
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +21 -22
M internal/capi/register.go +18 -15
@@ -1,6 +1,7 @@
11 package capi
22
33 import (
4+ "sync"
45 "unsafe"
56
67 "github.com/bnema/purego-pipewire/internal/loader"
@@ -31,25 +32,27 @@ func (d defaultImpl) PWMainLoopRun(loop unsafe.Pointer) int32 {
3132 }
3233
3334 var (
34- defaultAPI portout.CAPI
35- registered bool
35+ defaultAPI portout.CAPI
36+ registerOnce sync.Once
37+ registerErr error
3638 )
3739
3840 // Register loads the PipeWire libraries and registers all function bindings.
41+// It is safe to call from multiple goroutines; the initialization will only happen once.
3942 func Register() error {
40- if registered {
41- return nil
42- }
43- h, err := loader.Open()
44- if err != nil {
45- return err
46- }
47- if err := registerAll(h); err != nil {
48- return err
49- }
50- defaultAPI = defaultImpl{}
51- registered = true
52- return nil
43+ registerOnce.Do(func() {
44+ h, err := loader.Open()
45+ if err != nil {
46+ registerErr = err
47+ return
48+ }
49+ if err := registerAll(h); err != nil {
50+ registerErr = err
51+ return
52+ }
53+ defaultAPI = defaultImpl{}
54+ })
55+ return registerErr
5356 }
5457
5558 // Default returns the default CAPI instance after Register() has been called.
@@ -1,6 +1,7 @@
1 package capi1 package capi
2 2
3 import (3 import (
4+ "sync"
4 "unsafe"5 "unsafe"
5 6
6 "github.com/bnema/purego-pipewire/internal/loader"7 "github.com/bnema/purego-pipewire/internal/loader"
@@ -31,25 +32,27 @@ func (d defaultImpl) PWMainLoopRun(loop unsafe.Pointer) int32 {
31 }32 }
32 33
33 var (34 var (
34- defaultAPI portout.CAPI35+ defaultAPI portout.CAPI
35- registered bool36+ registerOnce sync.Once
37+ registerErr error
36 )38 )
37 39
38 // Register loads the PipeWire libraries and registers all function bindings.40 // Register loads the PipeWire libraries and registers all function bindings.
41+// It is safe to call from multiple goroutines; the initialization will only happen once.
39 func Register() error {42 func Register() error {
40- if registered {43+ registerOnce.Do(func() {
41- return nil44+ h, err := loader.Open()
42- }45+ if err != nil {
43- h, err := loader.Open()46+ registerErr = err
44- if err != nil {47+ return
45- return err48+ }
46- }49+ if err := registerAll(h); err != nil {
47- if err := registerAll(h); err != nil {50+ registerErr = err
48- return err51+ return
49- }52+ }
50- defaultAPI = defaultImpl{}53+ defaultAPI = defaultImpl{}
51- registered = true54+ })
52- return nil55+ return registerErr
53 }56 }
54 57
55 // Default returns the default CAPI instance after Register() has been called.58 // Default returns the default CAPI instance after Register() has been called.
M pipewire/interfaces.go +1 -1
@@ -9,6 +9,6 @@ type Runtime interface {
99
1010 // MainLoop is the public interface for PipeWire main loop operations.
1111 type MainLoop interface {
12- Run()
12+ Run() int32
1313 Destroy()
1414 }
@@ -9,6 +9,6 @@ type Runtime interface {
9 9
10 // MainLoop is the public interface for PipeWire main loop operations.10 // MainLoop is the public interface for PipeWire main loop operations.
11 type MainLoop interface {11 type MainLoop interface {
12- Run()12+ Run() int32
13 Destroy()13 Destroy()
14 }14 }
D pipewire/loop.go +0 -4
@@ -1,4 +0,0 @@
1-package pipewire
2-
3-// Loop provides additional main loop related types and constants.
4-// This file exists for future loop-related public API expansion.
@@ -1,4 +0,0 @@
1-package pipewire
2-
3-// Loop provides additional main loop related types and constants.
4-// This file exists for future loop-related public API expansion.
M pipewire/runtime.go +2 -2
@@ -33,8 +33,8 @@ type mainLoopWrapper struct {
3333 inner *core.MainLoop
3434 }
3535
36-func (mlw mainLoopWrapper) Run() {
37- mlw.inner.Run()
36+func (mlw mainLoopWrapper) Run() int32 {
37+ return mlw.inner.Run()
3838 }
3939
4040 func (mlw mainLoopWrapper) Destroy() {
@@ -33,8 +33,8 @@ type mainLoopWrapper struct {
33 inner *core.MainLoop33 inner *core.MainLoop
34 }34 }
35 35
36-func (mlw mainLoopWrapper) Run() {36+func (mlw mainLoopWrapper) Run() int32 {
37- mlw.inner.Run()37+ return mlw.inner.Run()
38 }38 }
39 39
40 func (mlw mainLoopWrapper) Destroy() {40 func (mlw mainLoopWrapper) Destroy() {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 8

↑↓ moveenter openesc close