github.com/turingchain2020/turingchain@v1.1.21/blockchain/task.go (about)

     1  // Copyright Turing Corp. 2018 All Rights Reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"errors"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/turingchain2020/turingchain/types"
    13  )
    14  
    15  //Task 任务
    16  type Task struct {
    17  	sync.Mutex
    18  	cond      *sync.Cond
    19  	start     int64
    20  	end       int64
    21  	isruning  bool
    22  	ticker    *time.Timer
    23  	timeout   time.Duration
    24  	cb        func()
    25  	donelist  map[int64]struct{}
    26  	timeoutcb func(height int64)
    27  }
    28  
    29  func newTask(timeout time.Duration) *Task {
    30  	t := &Task{}
    31  	t.timeout = timeout
    32  	t.ticker = time.NewTimer(t.timeout)
    33  	t.cond = sync.NewCond(t)
    34  	go t.tick()
    35  	return t
    36  }
    37  
    38  func (t *Task) tick() {
    39  	for {
    40  		t.cond.L.Lock()
    41  		for !t.isruning {
    42  			t.cond.Wait()
    43  		}
    44  		t.cond.L.Unlock()
    45  		_, ok := <-t.ticker.C
    46  		if !ok {
    47  			chainlog.Error("task is done", "timer ticker is stop", t.start)
    48  			continue
    49  		}
    50  		t.Lock()
    51  		if err := t.stop(false); err == nil {
    52  			if t.timeoutcb != nil {
    53  				go t.timeoutcb(t.start)
    54  			}
    55  			chainlog.Debug("task is done", "timer timeout is stop", t.start)
    56  		}
    57  		t.Unlock()
    58  	}
    59  }
    60  
    61  //InProgress 是否在执行
    62  func (t *Task) InProgress() bool {
    63  	t.Lock()
    64  	defer t.Unlock()
    65  	return t.isruning
    66  }
    67  
    68  //TimerReset 计时器重置
    69  func (t *Task) TimerReset(timeout time.Duration) {
    70  	t.TimerStop()
    71  	t.ticker.Reset(timeout)
    72  }
    73  
    74  //TimerStop 计时器停止
    75  func (t *Task) TimerStop() {
    76  	if !t.ticker.Stop() {
    77  		select {
    78  		case <-t.ticker.C:
    79  		default:
    80  		}
    81  	}
    82  }
    83  
    84  //Start 计时器启动
    85  func (t *Task) Start(start, end int64, cb func(), timeoutcb func(height int64)) error {
    86  	t.Lock()
    87  	defer t.Unlock()
    88  	if t.isruning {
    89  		return errors.New("task is running")
    90  	}
    91  	if start > end {
    92  		return types.ErrStartBigThanEnd
    93  	}
    94  	chainlog.Debug("task start:", "start", start, "end", end)
    95  	t.isruning = true
    96  	t.TimerReset(t.timeout)
    97  	t.start = start
    98  	t.end = end
    99  	t.cb = cb
   100  	t.timeoutcb = timeoutcb
   101  	t.donelist = make(map[int64]struct{})
   102  	t.cond.Signal()
   103  	return nil
   104  }
   105  
   106  //Done 任务完成
   107  func (t *Task) Done(height int64) {
   108  	t.Lock()
   109  	defer t.Unlock()
   110  	if !t.isruning {
   111  		return
   112  	}
   113  	if height >= t.start && height <= t.end {
   114  		chainlog.Debug("done", "height", height)
   115  		t.done(height)
   116  		t.TimerReset(t.timeout)
   117  	}
   118  }
   119  
   120  func (t *Task) stop(runcb bool) error {
   121  	if !t.isruning {
   122  		return errors.New("not running")
   123  	}
   124  	t.isruning = false
   125  	if t.cb != nil && runcb {
   126  		go t.cb()
   127  	}
   128  	t.TimerStop()
   129  	return nil
   130  }
   131  
   132  //Cancel 任务取消
   133  func (t *Task) Cancel() error {
   134  	t.Lock()
   135  	defer t.Unlock()
   136  	chainlog.Warn("----task is cancel----")
   137  	return t.stop(false)
   138  }
   139  
   140  func (t *Task) done(height int64) {
   141  	if height == t.start {
   142  		t.start = t.start + 1
   143  		for i := t.start; i <= t.end; i++ {
   144  			_, ok := t.donelist[i]
   145  			if !ok {
   146  				break
   147  			}
   148  			delete(t.donelist, i)
   149  			t.start = i + 1
   150  			//任务完成
   151  		}
   152  		if t.start > t.end {
   153  			chainlog.Debug("----task is done----")
   154  			err := t.stop(true)
   155  			if err != nil {
   156  				chainlog.Debug("----task stop error ----", "err", err)
   157  			}
   158  		}
   159  	}
   160  	t.donelist[height] = struct{}{}
   161  }