Add micro's own lua timer function micro.After()

Directly using Go's time.AfterFunc() from lua is tricky. First, it
requires the lua timer callback to explicitly lock ulua.Lock to prevent
races. Second, it requires the lua timer callback to explicitly redraw
the screen if the callback changes the screen contents (see #2923).

So instead provide micro's own timer API which ensures both
synchronization and redrawing on its own, instead of leaving this burden
to lua code. In fact, its implementation runs the lua timer callback in
the main micro's goroutine (i.e. from micro's perspective it is
synchronous, not asynchronous), so both redrawing and synchronization
are ensured automatically.

Fixes #2923
This commit is contained in:
Dmytro Maluka 2023-11-13 17:51:19 +01:00
parent 3dba23a348
commit 9089e9ec83
3 changed files with 17 additions and 0 deletions

View file

@ -2,6 +2,7 @@ package main
import (
"log"
"time"
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
@ -55,6 +56,11 @@ func luaImportMicro() *lua.LTable {
return action.Tabs
}))
ulua.L.SetField(pkg, "Lock", luar.New(ulua.L, &ulua.Lock))
ulua.L.SetField(pkg, "After", luar.New(ulua.L, func(t time.Duration, f func()) {
time.AfterFunc(t, func() {
timerChan <- f
})
}))
return pkg
}

View file

@ -46,6 +46,8 @@ var (
sigterm chan os.Signal
sighup chan os.Signal
timerChan chan func()
)
func InitFlags() {
@ -364,6 +366,8 @@ func main() {
signal.Notify(sigterm, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGABRT)
signal.Notify(sighup, syscall.SIGHUP)
timerChan = make(chan func())
// Here is the event loop which runs in a separate thread
go func() {
for {
@ -429,6 +433,8 @@ func DoEvent() {
for len(screen.DrawChan()) > 0 {
<-screen.DrawChan()
}
case f := <-timerChan:
f()
case <-sighup:
for _, b := range buffer.OpenBuffers {
if !b.Modified() {

View file

@ -121,6 +121,11 @@ The packages and functions are listed below (in Go type signatures):
current pane is not a BufPane.
- `CurTab() *Tab`: returns the current tab.
- `After(t time.Duration, f func())`: run function `f` in the background
after time `t` elapses. See https://pkg.go.dev/time#Duration for the
usage of `time.Duration`.
* `micro/config`
- `MakeCommand(name string, action func(bp *BufPane, args[]string),
completer buffer.Completer)`: