github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/cmd/generates/processes/process.go (about)

     1  /*
     2   * Copyright 2023 Wang Min Xiang
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * 	http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package processes
    19  
    20  import (
    21  	"context"
    22  	"github.com/aacfactory/errors"
    23  	"time"
    24  )
    25  
    26  var (
    27  	ErrAborted = errors.Warning("processes: abort")
    28  )
    29  
    30  func New() *Process {
    31  	return &Process{
    32  		units:    0,
    33  		steps:    make([]*Step, 0, 1),
    34  		resultCh: make(chan Result, 512),
    35  		closedCh: make(chan struct{}, 1),
    36  		cancel:   nil,
    37  	}
    38  }
    39  
    40  type ProcessController interface {
    41  	Steps() (n int64)
    42  	Units() (n int64)
    43  	Start(ctx context.Context) (results <-chan Result)
    44  	Abort(timeout time.Duration) (err error)
    45  }
    46  
    47  type Process struct {
    48  	units    int64
    49  	steps    []*Step
    50  	resultCh chan Result
    51  	closedCh chan struct{}
    52  	cancel   context.CancelFunc
    53  }
    54  
    55  func (p *Process) Add(name string, units ...Unit) {
    56  	no := int64(len(p.steps) + 1)
    57  	p.steps = append(p.steps, &Step{
    58  		no:       no,
    59  		name:     name,
    60  		num:      0,
    61  		units:    units,
    62  		resultCh: p.resultCh,
    63  	})
    64  	for _, step := range p.steps {
    65  		step.num = no
    66  	}
    67  	if units != nil {
    68  		p.units = p.units + int64(len(units))
    69  	}
    70  }
    71  
    72  func (p *Process) Steps() (n int64) {
    73  	n = int64(len(p.steps))
    74  	return
    75  }
    76  
    77  func (p *Process) Units() (n int64) {
    78  	n = p.units
    79  	return
    80  }
    81  
    82  func (p *Process) Start(ctx context.Context) (results <-chan Result) {
    83  	ctx, p.cancel = context.WithCancel(ctx)
    84  	go func(ctx context.Context, p *Process, result chan Result) {
    85  		for _, step := range p.steps {
    86  			stop := false
    87  			select {
    88  			case <-ctx.Done():
    89  				result <- Result{
    90  					StepNo:   0,
    91  					StepNum:  0,
    92  					StepName: "",
    93  					UnitNo:   0,
    94  					UnitNum:  0,
    95  					Data:     nil,
    96  					Error:    ErrAborted.WithCause(ctx.Err()),
    97  				}
    98  				stop = true
    99  				p.closedCh <- struct{}{}
   100  				break
   101  			default:
   102  				err := step.Execute(ctx)
   103  				if err != nil {
   104  					stop = true
   105  					p.closedCh <- struct{}{}
   106  				}
   107  				break
   108  			}
   109  			if stop {
   110  				break
   111  			}
   112  		}
   113  		close(result)
   114  		close(p.closedCh)
   115  	}(ctx, p, p.resultCh)
   116  	results = p.resultCh
   117  	return
   118  }
   119  
   120  func (p *Process) Abort(timeout time.Duration) (err error) {
   121  	if p.cancel == nil {
   122  		return
   123  	}
   124  	p.cancel()
   125  	select {
   126  	case <-time.After(timeout):
   127  		err = errors.Timeout("processes: abort timeout")
   128  		break
   129  	case <-p.closedCh:
   130  		break
   131  	}
   132  	return
   133  }
   134  
   135  func IsAbortErr(err error) (ok bool) {
   136  	ok = errors.Wrap(err).Contains(ErrAborted) || errors.Wrap(err).Contains(context.Canceled)
   137  	return
   138  }