cmd/tailscale: simplify non-blocking coalescing channels

Three variables are used everywhere a single non-blocking producer sends
values where only the latest is relevant:

    var (
	    mu sync.Mutex
	    latest T
	    notify = make(chan struct{}, 1)
    )

By draining the notification channel before sending through it, we
can simplify to just one channel:

    latest = make(chan T, 1)

Thanks to Chris Waldon for showing me this neat trick.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
pull/7/head
Elias Naur 5 years ago
parent dfe7b6c0a2
commit b151de039b

@ -7,7 +7,6 @@ package main
// JNI implementations of Java native callback methods. // JNI implementations of Java native callback methods.
import ( import (
"sync/atomic"
"unsafe" "unsafe"
"github.com/tailscale/tailscale-android/jni" "github.com/tailscale/tailscale-android/jni"
@ -33,20 +32,12 @@ var (
onDisconnect = make(chan jni.Object) onDisconnect = make(chan jni.Object)
// onConnectivityChange is notified every time the network // onConnectivityChange is notified every time the network
// conditions change. // conditions change.
onConnectivityChange = make(chan struct{}, 1) onConnectivityChange = make(chan bool, 1)
// onGoogleToken receives google ID tokens. // onGoogleToken receives google ID tokens.
onGoogleToken = make(chan string) onGoogleToken = make(chan string)
) )
var (
connected atomic.Value
)
func init() {
connected.Store(true)
}
const ( const (
// Request codes for Android callbacks. // Request codes for Android callbacks.
// requestSignin is for Google Sign-In. // requestSignin is for Google Sign-In.
@ -98,12 +89,12 @@ func Java_com_tailscale_ipn_IPNService_disconnect(env *C.JNIEnv, this C.jobject)
} }
//export Java_com_tailscale_ipn_App_onConnectivityChanged //export Java_com_tailscale_ipn_App_onConnectivityChanged
func Java_com_tailscale_ipn_App_onConnectivityChanged(env *C.JNIEnv, cls C.jclass, newConnected C.jboolean) { func Java_com_tailscale_ipn_App_onConnectivityChanged(env *C.JNIEnv, cls C.jclass, connected C.jboolean) {
connected.Store(newConnected == C.JNI_TRUE)
select { select {
case onConnectivityChange <- struct{}{}: case <-onConnectivityChange:
default: default:
} }
onConnectivityChange <- connected == C.JNI_TRUE
} }
//export Java_com_tailscale_ipn_QuickToggleService_onTileClick //export Java_com_tailscale_ipn_QuickToggleService_onTileClick

@ -10,7 +10,6 @@ import (
"log" "log"
"sort" "sort"
"strings" "strings"
"sync"
"time" "time"
"unsafe" "unsafe"
@ -36,22 +35,16 @@ type App struct {
store *stateStore store *stateStore
// updates is notifies whenever netState or browseURL changes. // netStates receives the most recent network state.
updates chan struct{} netStates chan BackendState
// prefs receives new preferences from the backend.
prefs chan *ipn.Prefs
// browseURLs receives URLs when the backend wants to browse.
browseURLs chan string
// backend is the channel for events from the frontend to the // backend is the channel for events from the frontend to the
// backend. // backend.
backendEvents chan UIEvent backendEvents chan UIEvent
// mu protects the following fields.
mu sync.Mutex
// netState is the most recent network state.
netState BackendState
// browseURL is set whenever the backend wants to
// browse.
browseURL *string
// prefs is set when new preferences arrive.
prefs *ipn.Prefs
} }
var ( var (
@ -117,9 +110,11 @@ var backendEvents = make(chan UIEvent)
func main() { func main() {
a := &App{ a := &App{
jvm: (*jni.JVM)(unsafe.Pointer(app.JavaVM())), jvm: (*jni.JVM)(unsafe.Pointer(app.JavaVM())),
appCtx: jni.Object(app.AppContext()), appCtx: jni.Object(app.AppContext()),
updates: make(chan struct{}, 1), netStates: make(chan BackendState, 1),
browseURLs: make(chan string, 1),
prefs: make(chan *ipn.Prefs, 1),
} }
err := jni.Do(a.jvm, func(env *jni.Env) error { err := jni.Do(a.jvm, func(env *jni.Env) error {
loader := jni.ClassLoaderFor(env, a.appCtx) loader := jni.ClassLoaderFor(env, a.appCtx)
@ -304,8 +299,8 @@ func (a *App) runBackend() error {
notifyVPNClosed() notifyVPNClosed()
} }
} }
case <-onConnectivityChange: case connected := <-onConnectivityChange:
state.LostInternet = !connected.Load().(bool) state.LostInternet = !connected
if b != nil { if b != nil {
go b.LinkChange() go b.LinkChange()
} }
@ -461,13 +456,11 @@ func (a *App) notifyExpiry(service jni.Object, expiry time.Time) *time.Timer {
} }
func (a *App) notify(state BackendState) { func (a *App) notify(state BackendState) {
a.mu.Lock()
a.netState = state
a.mu.Unlock()
select { select {
case a.updates <- struct{}{}: case <-a.netStates:
default: default:
} }
a.netStates <- state
ready := jni.Bool(state.State >= ipn.Stopped) ready := jni.Bool(state.State >= ipn.Stopped)
if err := a.callVoidMethod(a.appCtx, "setTileReady", "(Z)V", jni.Value(ready)); err != nil { if err := a.callVoidMethod(a.appCtx, "setTileReady", "(Z)V", jni.Value(ready)); err != nil {
fatalErr(err) fatalErr(err)
@ -475,27 +468,23 @@ func (a *App) notify(state BackendState) {
} }
func (a *App) setPrefs(prefs *ipn.Prefs) { func (a *App) setPrefs(prefs *ipn.Prefs) {
a.mu.Lock()
a.prefs = prefs
wantRunning := jni.Bool(prefs.WantRunning) wantRunning := jni.Bool(prefs.WantRunning)
a.mu.Unlock()
if err := a.callVoidMethod(a.appCtx, "setTileStatus", "(Z)V", jni.Value(wantRunning)); err != nil { if err := a.callVoidMethod(a.appCtx, "setTileStatus", "(Z)V", jni.Value(wantRunning)); err != nil {
fatalErr(err) fatalErr(err)
} }
select { select {
case a.updates <- struct{}{}: case <-a.prefs:
default: default:
} }
a.prefs <- prefs
} }
func (a *App) setURL(url string) { func (a *App) setURL(url string) {
a.mu.Lock()
a.browseURL = &url
a.mu.Unlock()
select { select {
case a.updates <- struct{}{}: case <-a.browseURLs:
default: default:
} }
a.browseURLs <- url
} }
func (a *App) runUI() error { func (a *App) runUI() error {
@ -542,20 +531,16 @@ func (a *App) runUI() error {
w.Invalidate() w.Invalidate()
} }
} }
case <-a.updates: case p := <-a.prefs:
a.mu.Lock() ui.enabled.Value = p.WantRunning
w.Invalidate()
case state.browseURL = <-a.browseURLs:
ui.signinType = noSignin
w.Invalidate()
a.updateState(activity, state)
case newState := <-a.netStates:
oldState := state.backend.State oldState := state.backend.State
state.backend = a.netState state.backend = newState
if a.browseURL != nil {
state.browseURL = *a.browseURL
a.browseURL = nil
ui.signinType = noSignin
}
if a.prefs != nil {
ui.enabled.Value = a.prefs.WantRunning
a.prefs = nil
}
a.mu.Unlock()
a.updateState(activity, state) a.updateState(activity, state)
w.Invalidate() w.Invalidate()
if activity != 0 { if activity != 0 {

Loading…
Cancel
Save