v0.33.1 9577fee

pool.go RawHistoryFolder

1package cef
2
3import (
4	"context"
5	"errors"
6	"fmt"
7	"sync"
8
9	"github.com/bnema/dumber/internal/application/port"
10	"github.com/bnema/dumber/internal/logging"
11)
12
13// Compile-time interface check.
14var _ port.WebViewPool = (*WebViewPool)(nil)
15
16// errPoolClosed is returned when an operation is attempted on a closed pool.
17var errPoolClosed = errors.New("cef: webview pool is closed")
18
19// WebViewPool manages a pool of pre-created WebViews for fast tab creation.
20// In Phase 1 the pool is simple: Acquire pops or creates, Release destroys.
21type WebViewPool struct {
22	factory *WebViewFactory
23	mu      sync.Mutex
24	pool    []*WebView
25	closed  bool
26}
27
28// newWebViewPool returns a pool backed by the given factory.
29func newWebViewPool(factory *WebViewFactory) *WebViewPool {
30	return &WebViewPool{
31		factory: factory,
32	}
33}
34
35// Acquire obtains a WebView from the pool. If a pre-warmed WebView is
36// available it is returned immediately; otherwise a new one is created.
37func (p *WebViewPool) Acquire(ctx context.Context) (port.WebView, error) {
38	p.mu.Lock()
39	if p.closed {
40		p.mu.Unlock()
41		return nil, errPoolClosed
42	}
43
44	if len(p.pool) > 0 {
45		wv := p.pool[len(p.pool)-1]
46		p.pool = p.pool[:len(p.pool)-1]
47		p.mu.Unlock()
48		wv.generation.Add(1)
49		return wv, nil
50	}
51	p.mu.Unlock()
52
53	return p.factory.Create(ctx)
54}
55
56// Release destroys the WebView. In Phase 1 there is no reuse — the browser
57// is closed and all resources are freed immediately.
58func (p *WebViewPool) Release(wv port.WebView) {
59	if wv == nil {
60		return
61	}
62	wv.Destroy()
63}
64
65// Prewarm creates count WebViews synchronously and adds them to the pool.
66func (p *WebViewPool) Prewarm(count int) {
67	if count <= 0 {
68		return
69	}
70
71	ctx := p.factory.engine.ctx
72	log := logging.FromContext(ctx)
73	views := make([]*WebView, 0, count)
74	for range count {
75		wv, err := p.factory.Create(ctx)
76		if err != nil {
77			log.Warn().Err(err).Msg("cef: pool prewarm failed to create WebView")
78			continue
79		}
80		cefWV, ok := wv.(*WebView)
81		if !ok {
82			log.Warn().Str("concrete_type", fmt.Sprintf("%T", wv)).Msg("cef: pool prewarm got unexpected WebView type")
83			wv.Destroy()
84			continue
85		}
86		views = append(views, cefWV)
87	}
88
89	p.mu.Lock()
90	defer p.mu.Unlock()
91	if p.closed {
92		for _, wv := range views {
93			wv.Destroy()
94		}
95		return
96	}
97	p.pool = append(p.pool, views...)
98}
99
100// PrewarmAsync schedules WebView creation. In Phase 1 this calls Prewarm
101// synchronously; a future phase will use the GTK idle loop to avoid blocking.
102// TODO(phase2): schedule creation via glib.IdleAdd for non-blocking startup.
103func (p *WebViewPool) PrewarmAsync(_ context.Context, count int) {
104	p.Prewarm(count)
105}
106
107func (p *WebViewPool) Size() int {
108	p.mu.Lock()
109	defer p.mu.Unlock()
110	return len(p.pool)
111}
112
113// pooledViewBrowserReady reports whether a pooled reserve holds a live
114// browser. Wrapper-only reserves (allocated struct, no OnAfterCreated yet)
115// are never reported browser-ready. Viewport validity and first blank frame
116// require native validation and stay outside this predicate.
117func pooledViewBrowserReady(wv *WebView) bool {
118	if wv == nil || wv.destroyed.Load() {
119		return false
120	}
121	wv.mu.RLock()
122	defer wv.mu.RUnlock()
123	return wv.browser != nil
124}
125
126// Close shuts down the pool and destroys all pooled WebViews.
127func (p *WebViewPool) Close() {
128	p.mu.Lock()
129	if p.closed {
130		p.mu.Unlock()
131		return
132	}
133	p.closed = true
134	views := p.pool
135	p.pool = nil
136	p.mu.Unlock()
137
138	for _, wv := range views {
139		wv.Destroy()
140	}
141}

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 8

Tags 42

↑↓ moveenter openesc close