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  }