File: delay_task.go

package info (click to toggle)
go-dlib 5.6.0.9%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 3,200 kB
  • sloc: ansic: 4,664; xml: 1,456; makefile: 20; sh: 15
file content (125 lines) | stat: -rw-r--r-- 2,839 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
 *
 * Author:     jouyouyun <jouyouwen717@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tasker

import (
	"fmt"
	"reflect"
	"sync"
	"time"
)

type DelayTask struct {
	timer    *time.Timer
	handler  reflect.Value
	argsType []reflect.Type

	duration time.Duration

	termination chan struct{}
	locker      sync.Mutex
}

func NewDelayTask(duration time.Duration,
	cb interface{}) (*DelayTask, error) {
	handler, argsType, err := handleFuncSignature(cb)
	if err != nil {
		return nil, err
	}
	return &DelayTask{
		handler:  *handler,
		argsType: argsType,
		duration: duration,
	}, nil
}

func (task *DelayTask) Start(args ...interface{}) error {
	// if started, stop it, else nothing to do
	task.Stop()

	task.locker.Lock()
	defer task.locker.Unlock()
	if len(task.argsType) != len(args) {
		return fmt.Errorf("argument length expected %d, but got %d",
			len(task.argsType), len(args))
	}
	// check each arg type
	for i, t := range task.argsType {
		t0 := reflect.TypeOf(args[i])
		if t != t0 {
			return fmt.Errorf("args[%d] type expected %s, but got %s",
				i, t, t0)
		}
	}
	var values = make([]reflect.Value, len(args))
	for i, v := range args {
		values[i] = reflect.ValueOf(v)
	}

	task.doStart(values)
	return nil
}

func (task *DelayTask) doStart(values []reflect.Value) {
	task.termination = make(chan struct{})
	task.timer = time.NewTimer(task.duration)
	go func() {
		exit := task.termination
		timer := task.timer
		select {
		case <-exit:
			return
		case <-timer.C:
			task.handler.Call(values)
			return
		}
	}()
}

func (task *DelayTask) Stop() {
	task.locker.Lock()
	defer task.locker.Unlock()
	if task.termination != nil {
		close(task.termination)
		task.termination = nil
		// TODO: delete
		time.Sleep(time.Millisecond * 50)
	}
	if task.timer != nil {
		task.timer.Stop()
	}
}

func handleFuncSignature(f interface{}) (*reflect.Value,
	[]reflect.Type, error) {
	fn := reflect.ValueOf(f)
	if fn.Kind() != reflect.Func {
		return nil, nil, fmt.Errorf("not a function")
	}

	fnType := fn.Type()
	fnNum := fnType.NumIn()
	argTypes := make([]reflect.Type, fnNum)
	for i := 0; i < fnNum; i++ {
		argTypes[i] = fnType.In(i)
	}

	return &fn, argTypes, nil
}