Skip to content

Commit 9f200f3

Browse files
committed
synchronize renderwork. call notifyfn when we get new render work from an async context...
1 parent c3fa716 commit 9f200f3

4 files changed

Lines changed: 88 additions & 16 deletions

File tree

tsunami/engine/clientimpl.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ const TsunamiListenAddrEnvVar = "TSUNAMI_LISTENADDR"
2626
const DefaultListenAddr = "localhost:0"
2727
const DefaultComponentName = "App"
2828

29+
const NotifyMaxCadence = 10 * time.Millisecond
30+
const NotifyDebounceTime = 500 * time.Microsecond
31+
const NotifyMaxDebounceTime = 2 * time.Millisecond
32+
2933
type ssEvent struct {
3034
Event string
3135
Data []byte
@@ -218,6 +222,10 @@ func (c *ClientImpl) SendAsyncInitiation() error {
218222
}
219223
}
220224

225+
func (c *ClientImpl) notifyAsyncRenderWork() {
226+
// TODO
227+
}
228+
221229
func makeNullRendered() *rpctypes.RenderedElem {
222230
return &rpctypes.RenderedElem{WaveId: uuid.New().String(), Tag: vdom.WaveNullTag}
223231
}

tsunami/engine/globalctx.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ import (
1010
"github.com/wavetermdev/waveterm/tsunami/vdom"
1111
)
1212

13+
const (
14+
GlobalContextType_async = "async"
15+
GlobalContextType_render = "render"
16+
GlobalContextType_effect = "effect"
17+
GlobalContextType_event = "event"
18+
)
19+
1320
// is set ONLY when we're in the render function of a component
1421
// used for hooks, and automatic dependency tracking
1522
var globalRenderContext *RenderContextImpl
@@ -123,3 +130,30 @@ func GetGlobalEffectContext() *EffectContextImpl {
123130
}
124131
return globalEffectContext
125132
}
133+
134+
// inContextType returns the current global context type.
135+
// Returns one of:
136+
// - GlobalContextType_render: when in a component render function
137+
// - GlobalContextType_event: when in an event handler
138+
// - GlobalContextType_effect: when in an effect function
139+
// - GlobalContextType_async: when not in any specific context (default/async)
140+
func inContextType() string {
141+
globalCtxMutex.Lock()
142+
defer globalCtxMutex.Unlock()
143+
144+
gid := goid.Get()
145+
146+
if globalRenderContext != nil && gid == globalRenderGoId {
147+
return GlobalContextType_render
148+
}
149+
150+
if globalEventContext != nil && gid == globalEventGoId {
151+
return GlobalContextType_event
152+
}
153+
154+
if globalEffectContext != nil && gid == globalEffectGoId {
155+
return GlobalContextType_effect
156+
}
157+
158+
return GlobalContextType_async
159+
}

tsunami/engine/hooks.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,14 @@ func UseEffect(vc *RenderContextImpl, fn func() func(), deps []any) {
125125
hookVal.Init = true
126126
hookVal.Fn = fn
127127
hookVal.Deps = deps
128-
vc.Root.AddEffectWork(vc.GetCompWaveId(), hookVal.Idx, compTag)
128+
vc.Root.addEffectWork(vc.GetCompWaveId(), hookVal.Idx, compTag)
129129
return
130130
}
131131
// If deps is nil, always run (like React with no dependency array)
132132
if deps == nil {
133133
hookVal.Fn = fn
134134
hookVal.Deps = deps
135-
vc.Root.AddEffectWork(vc.GetCompWaveId(), hookVal.Idx, compTag)
135+
vc.Root.addEffectWork(vc.GetCompWaveId(), hookVal.Idx, compTag)
136136
return
137137
}
138138

@@ -141,7 +141,7 @@ func UseEffect(vc *RenderContextImpl, fn func() func(), deps []any) {
141141
}
142142
hookVal.Fn = fn
143143
hookVal.Deps = deps
144-
vc.Root.AddEffectWork(vc.GetCompWaveId(), hookVal.Idx, compTag)
144+
vc.Root.addEffectWork(vc.GetCompWaveId(), hookVal.Idx, compTag)
145145
}
146146

147147
func UseResync(vc *RenderContextImpl) bool {

tsunami/engine/rootelem.go

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,50 @@ type RootElem struct {
3636
Root *ComponentImpl
3737
RenderTs int64
3838
AppTitle string
39-
CFuncs map[string]any
39+
CFuncs map[string]any // component name => render function
4040
CompMap map[string]*ComponentImpl // component waveid -> component
4141
EffectWorkQueue []*EffectWorkElem
42-
NeedsRenderMap map[string]bool
43-
Atoms map[string]genAtom
42+
needsRenderMap map[string]bool // key: waveid
43+
needsRenderLock sync.Mutex
44+
Atoms map[string]genAtom // key: atomName
4445
atomLock sync.Mutex
4546
RefOperations []vdom.VDomRefOperation
4647
Client *ClientImpl
4748
}
4849

49-
func (r *RootElem) AddRenderWork(id string) {
50-
if r.NeedsRenderMap == nil {
51-
r.NeedsRenderMap = make(map[string]bool)
50+
func (r *RootElem) addRenderWork(id string) {
51+
defer func() {
52+
if inContextType() == GlobalContextType_async {
53+
r.Client.notifyAsyncRenderWork()
54+
}
55+
}()
56+
57+
r.needsRenderLock.Lock()
58+
defer r.needsRenderLock.Unlock()
59+
60+
if r.needsRenderMap == nil {
61+
r.needsRenderMap = make(map[string]bool)
5262
}
53-
r.NeedsRenderMap[id] = true
63+
r.needsRenderMap[id] = true
5464
}
5565

56-
func (r *RootElem) AddEffectWork(id string, effectIndex int, compTag string) {
66+
func (r *RootElem) getAndClearRenderWork() []string {
67+
r.needsRenderLock.Lock()
68+
defer r.needsRenderLock.Unlock()
69+
70+
if len(r.needsRenderMap) == 0 {
71+
return nil
72+
}
73+
74+
ids := make([]string, 0, len(r.needsRenderMap))
75+
for id := range r.needsRenderMap {
76+
ids = append(ids, id)
77+
}
78+
r.needsRenderMap = nil
79+
return ids
80+
}
81+
82+
func (r *RootElem) addEffectWork(id string, effectIndex int, compTag string) {
5783
r.EffectWorkQueue = append(r.EffectWorkQueue, &EffectWorkElem{WaveId: id, EffectIndex: effectIndex, CompTag: compTag})
5884
}
5985

@@ -135,8 +161,12 @@ func (r *RootElem) AtomAddRenderWork(atomName string) {
135161
if !ok {
136162
return
137163
}
138-
for _, compId := range atom.GetUsedBy() {
139-
r.AddRenderWork(compId)
164+
usedBy := atom.GetUsedBy()
165+
if len(usedBy) == 0 {
166+
return
167+
}
168+
for _, compId := range usedBy {
169+
r.addRenderWork(compId)
140170
}
141171
}
142172

@@ -356,8 +386,8 @@ func (r *RootElem) RunWork(opts *RenderOpts) {
356386
r.runEffect(work, hook)
357387
}
358388
// now check if we need a render
359-
if len(r.NeedsRenderMap) > 0 {
360-
r.NeedsRenderMap = nil
389+
renderIds := r.getAndClearRenderWork()
390+
if len(renderIds) > 0 {
361391
r.render(r.Root.Elem, &r.Root, "root", opts)
362392
}
363393
}
@@ -392,7 +422,7 @@ func (r *RootElem) UpdateRef(updateRef rpctypes.VDomRefUpdate) {
392422
}
393423
ref.HasCurrent = updateRef.HasCurrent
394424
ref.Position = updateRef.Position
395-
r.AddRenderWork(waveId)
425+
r.addRenderWork(waveId)
396426
}
397427

398428
func (r *RootElem) QueueRefOp(op vdom.VDomRefOperation) {

0 commit comments

Comments
 (0)