Skip to content

Commit a373dde

Browse files
authored
Merge pull request #261 from rstudio/fix/task-register-after-handle
fix: fail fast when task is registered after Handle()
2 parents 96739c3 + 5975821 commit a373dde

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

pkg/rselection/taskhandler.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import (
1313
"github.com/rstudio/platform-lib/v3/pkg/rsnotify/broadcaster"
1414
)
1515

16+
// logFatalf is used for fatal errors and can be overridden in tests.
17+
var logFatalf = log.Fatalf
18+
1619
const (
1720
TaskTypePersistent uint8 = 0
1821
TaskTypeScheduled uint8 = 1
@@ -64,9 +67,15 @@ func NewGenericTaskHandler(cfg GenericTaskHandlerConfig) *GenericTaskHandler {
6467
}
6568
}
6669

70+
// Register adds a task to the handler. It must be called before Handle().
71+
// Calling Register after Handle() or registering the same task name twice
72+
// will cause the program to exit via log.Fatalf.
6773
func (h *GenericTaskHandler) Register(name string, task Task) {
74+
if h.cancel != nil {
75+
logFatalf("Task %q registered after Handle() was called; it will never run", name)
76+
}
6877
if _, ok := h.tasks[name]; ok {
69-
log.Fatalf("Attempted to register a task %s that is already registered", name)
78+
logFatalf("Attempted to register a task %s that is already registered", name)
7079
}
7180
h.tasks[name] = task
7281
}

pkg/rselection/taskhandler_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package rselection
44

55
import (
66
"context"
7+
"fmt"
78
"time"
89

910
"github.com/fortytw2/leaktest"
@@ -50,6 +51,37 @@ func (t *TestTaskPersistent) Run(ctx context.Context, b broadcaster.Broadcaster)
5051
}
5152
}
5253

54+
func (s *TaskHandlerSuite) TestRegisterAfterHandleFatals(c *check.C) {
55+
defer leaktest.Check(c)
56+
57+
handler := NewGenericTaskHandler(GenericTaskHandlerConfig{})
58+
handler.Handle(nil)
59+
60+
// Registering a task after Handle() should fatal.
61+
// Capture log.Fatalf by replacing it temporarily.
62+
var fatalMsg string
63+
origFatalf := logFatalf
64+
logFatalf = func(format string, v ...interface{}) {
65+
fatalMsg = fmt.Sprintf(format, v...)
66+
}
67+
defer func() { logFatalf = origFatalf }()
68+
69+
handler.Register("late-task", &TestTask{
70+
GenericTask: GenericTask{
71+
TaskName: "late-task",
72+
TaskType: TaskTypeScheduled,
73+
TaskSchedule: &IntervalSchedule{
74+
Ticker: make(chan time.Time),
75+
},
76+
},
77+
ran: new(int),
78+
n: make(chan bool),
79+
})
80+
81+
c.Assert(fatalMsg, check.Matches, `.*registered after Handle\(\).*`)
82+
handler.Stop()
83+
}
84+
5385
func (s *TaskHandlerSuite) TestGenericTaskHandler(c *check.C) {
5486
defer leaktest.Check(c)
5587

0 commit comments

Comments
 (0)