github.com/jmigpin/editor@v1.6.0/core/erowexec.go (about) 1 package core 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "sync" 8 9 "github.com/jmigpin/editor/ui" 10 ) 11 12 ////godebug:annotatefile 13 14 type ERowExec struct { 15 erow *ERow 16 mu struct { 17 sync.Mutex 18 cancel context.CancelFunc 19 fnWait sync.WaitGroup // added while locked 20 } 21 } 22 23 func NewERowExec(erow *ERow) *ERowExec { 24 ee := &ERowExec{erow: erow} 25 ee.mu.cancel = func() {} 26 return ee 27 } 28 29 //---------- 30 31 func (ee *ERowExec) RunAsync(fn func(context.Context, io.ReadWriter) error) { 32 // Note: textarea w.close() (textareawriter) could deadlock if runasync() is not on own goroutine. If w.close waits for UI goroutine to finish and runasync() is currently occupying it (w.close called after a runasync(), just that the UI goroutine is not getting released). 33 // launching in a goroutine allows RunAsync() itself to be called from a uigoroutine since this func will return immediately 34 go ee.runAsync2(fn) 35 } 36 37 func (ee *ERowExec) runAsync2(fn func(context.Context, io.ReadWriter) error) { 38 ee.mu.Lock() 39 defer ee.mu.Unlock() 40 41 // cancel and wait for previous if any 42 ee.mu.cancel() 43 ee.mu.fnWait.Wait() 44 45 // new context 46 ctx, cancel := context.WithCancel(ee.erow.ctx) 47 ee.mu.cancel = cancel 48 49 rwc := ee.erow.TextAreaReadWriteCloser() 50 51 ee.mu.fnWait.Add(1) 52 go func() { 53 defer ee.mu.fnWait.Done() 54 55 // indicate the row is running 56 ee.erow.Ed.UI.RunOnUIGoRoutine(func() { 57 ee.erow.Row.SetState(ui.RowStateExecuting, true) 58 ee.erow.Row.TextArea.SetStrClearHistory("") 59 ee.erow.Row.TextArea.ClearPos() 60 }) 61 62 err := fn(ctx, rwc) 63 if err != nil { 64 fmt.Fprintf(rwc, "# error: %v\n", err) 65 } 66 67 // clear cancel resources 68 cancel() 69 70 if err := rwc.Close(); err != nil { 71 ee.erow.Ed.Error(err) 72 } 73 74 ee.erow.Ed.UI.RunOnUIGoRoutine(func() { 75 ee.erow.Row.SetState(ui.RowStateExecuting, false) 76 }) 77 }() 78 } 79 80 //---------- 81 82 func (ee *ERowExec) Stop() { 83 ee.mu.Lock() 84 defer ee.mu.Unlock() 85 if ee.mu.cancel != nil { 86 ee.mu.cancel() 87 } 88 }