registry.go

v0.4.0
Doc Versions Source
1
package async
2
3
import (
4
	"slices"
5
	"sync"
6
	"sync/atomic"
7
	"time"
8
9
	"go.bigb.es/auxilia/goroutine"
10
)
11
12
// TaskInfo holds metadata about a running task for debugging and investigation.
13
type TaskInfo struct {
14
	ID          uint64
15
	Tags        []string
16
	StartTime   time.Time
17
	GoroutineID uint64
18
	done        <-chan struct{}
19
}
20
21
// IsRunning reports whether the task is still running.
22
func (ti *TaskInfo) IsRunning() bool {
23
	select {
24
	case <-ti.done:
25
		return false
26
	default:
27
		return true
28
	}
29
}
30
31
// Stacktrace returns the stack trace of the goroutine running this task.
32
// Returns empty string if the task has already finished.
33
func (ti *TaskInfo) Stacktrace() string {
34
	if !ti.IsRunning() {
35
		return ""
36
	}
37
	return goroutine.Stack(ti.GoroutineID)
38
}
39
40
var taskIDSeq atomic.Uint64
41
42
// Registry stores metadata about all running tasks for investigation.
43
type Registry struct {
44
	mu    sync.RWMutex
45
	tasks map[uint64]*TaskInfo
46
}
47
48
// NewRegistry creates a new [Registry].
49
func NewRegistry() *Registry {
50
	return &Registry{
51
		tasks: make(map[uint64]*TaskInfo),
52
	}
53
}
54
55
func (r *Registry) register(done <-chan struct{}, goroutineID uint64, tags []string) uint64 {
56
	id := taskIDSeq.Add(1)
57
	info := &TaskInfo{
58
		ID:          id,
59
		Tags:        tags,
60
		StartTime:   time.Now(),
61
		GoroutineID: goroutineID,
62
		done:        done,
63
	}
64
65
	r.mu.Lock()
66
	r.tasks[id] = info
67
	r.mu.Unlock()
68
69
	go func() {
70
		<-done
71
		r.mu.Lock()
72
		delete(r.tasks, id)
73
		r.mu.Unlock()
74
	}()
75
76
	return id
77
}
78
79
// Get returns the [TaskInfo] for a task by ID, or nil if not found.
80
func (r *Registry) Get(id uint64) *TaskInfo {
81
	r.mu.RLock()
82
	defer r.mu.RUnlock()
83
	return r.tasks[id]
84
}
85
86
// All returns a snapshot of all currently running tasks.
87
func (r *Registry) All() []*TaskInfo {
88
	r.mu.RLock()
89
	defer r.mu.RUnlock()
90
91
	out := make([]*TaskInfo, 0, len(r.tasks))
92
	for _, info := range r.tasks {
93
		out = append(out, info)
94
	}
95
	return out
96
}
97
98
// ByTag returns all running tasks that have the given tag.
99
func (r *Registry) ByTag(tag string) []*TaskInfo {
100
	r.mu.RLock()
101
	defer r.mu.RUnlock()
102
103
	var out []*TaskInfo
104
	for _, info := range r.tasks {
105
		if slices.Contains(info.Tags, tag) {
106
			out = append(out, info)
107
		}
108
	}
109
	return out
110
}
111
112
// Len returns the number of currently registered tasks.
113
func (r *Registry) Len() int {
114
	r.mu.RLock()
115
	defer r.mu.RUnlock()
116
	return len(r.tasks)
117
}
118

Source Files