@@ -42,12 +42,6 @@ type Watchdog struct {
42
42
// and updating may depend on changing external parameters
43
43
// (such as configuration file).
44
44
provider Provider
45
- // instanceStateMutex used to avoid a race condition under started and shouldStop fields.
46
- instanceStateMutex sync.Mutex
47
- // started indicates whether the Watchdog has started an Instance.
48
- started bool
49
- // shouldStop indicates whether the Watchdog should stop the Instance.
50
- shouldStop bool
51
45
}
52
46
53
47
// NewWatchdog creates a new instance of Watchdog.
@@ -57,48 +51,61 @@ func NewWatchdog(restartable bool, restartTimeout time.Duration, logger *ttlog.L
57
51
provider : provider }
58
52
59
53
wd .done = make (chan bool , 1 )
60
- wd .shouldStop = false
61
54
62
55
return & wd
63
56
}
64
57
65
58
// Start starts the Instance and signal handling.
66
- func (wd * Watchdog ) Start () {
59
+ func (wd * Watchdog ) Start (preStartAction func () error ) error {
60
+ var err error
61
+ // Create Instance.
62
+ if wd .Instance , err = wd .provider .CreateInstance (wd .logger ); err != nil {
63
+ wd .logger .Printf (`Watchdog(ERROR): "%v".` , err )
64
+ return err
65
+ }
66
+ wd .logger = wd .Instance .logger
67
+ // The signal handling loop must be started before the instance
68
+ // get started for avoiding a race condition between tt start
69
+ // and tt stop. This way we avoid a situation when we receive
70
+ // a signal before starting a handler for it.
71
+ wd .startSignalHandling ()
72
+
73
+ if err = preStartAction (); err != nil {
74
+ wd .logger .Printf (`Pre-start action error: %v` , err )
75
+ // Finish the signal handling goroutine.
76
+ wd .done <- true
77
+ return err
78
+ }
79
+
67
80
// The Instance must be restarted on completion if the "restartable"
68
81
// parameter is set to "true".
69
82
for {
70
83
var err error
71
- // Create Instance.
72
- if wd .Instance , err = wd .provider .CreateInstance (wd .logger ); err != nil {
73
- wd .logger .Printf (`Watchdog(ERROR): "%v".` , err )
74
- break
75
- }
76
- wd .logger = wd .Instance .logger
77
84
78
- wd .stateMutex .Lock ()
79
- if ! wd .shouldStop {
85
+ wd .Instance . stateMutex .Lock ()
86
+ if ! wd .Instance . shouldStop {
80
87
// Start the Instance.
81
88
if err := wd .Instance .Start (); err != nil {
82
89
wd .logger .Printf (`Watchdog(ERROR): "%v".` , err )
83
- wd .stateMutex .Unlock ()
90
+ wd .Instance . stateMutex .Unlock ()
84
91
break
85
92
}
86
- wd .started = true
93
+ wd .Instance . started = true
87
94
} else {
88
95
wd .logger .Printf (`Watchdog(ERROR): terminated before instance start.` )
89
- wd .stateMutex .Unlock ()
90
- return
96
+ wd .Instance . stateMutex .Unlock ()
97
+ return nil
91
98
}
92
- wd .stateMutex .Unlock ()
99
+ wd .Instance . stateMutex .Unlock ()
93
100
94
101
// Wait while the Instance will be terminated.
95
102
if err := wd .Instance .Wait (); err != nil {
96
103
wd .logger .Printf (`Watchdog(WARN): "%v".` , err )
97
104
}
98
105
99
- wd .stateMutex .Lock ()
100
- wd .started = false
101
- wd .stateMutex .Unlock ()
106
+ wd .Instance . stateMutex .Lock ()
107
+ wd .Instance . started = false
108
+ wd .Instance . stateMutex .Unlock ()
102
109
103
110
// Set Instance process completion indication.
104
111
wd .done <- true
@@ -111,7 +118,7 @@ func (wd *Watchdog) Start() {
111
118
wd .logger .Println ("Watchdog(ERROR): can't check if the instance is restartable." )
112
119
break
113
120
}
114
- if wd .shouldStop || ! restartable {
121
+ if wd .Instance . shouldStop || ! restartable {
115
122
wd .logger .Println ("Watchdog(INFO): the Instance has shutdown." )
116
123
break
117
124
}
@@ -124,14 +131,22 @@ func (wd *Watchdog) Start() {
124
131
}
125
132
time .Sleep (wd .restartTimeout )
126
133
127
- wd .shouldStop = false
134
+ wd .Instance .shouldStop = false
135
+
136
+ // Create Instance.
137
+ if wd .Instance , err = wd .provider .CreateInstance (wd .logger ); err != nil {
138
+ wd .logger .Printf (`Watchdog(ERROR): "%v".` , err )
139
+ return err
140
+ }
141
+ wd .logger = wd .Instance .logger
128
142
// Before the restart of an instance start a new signal handling loop.
129
- wd .StartSignalHandling ()
143
+ wd .startSignalHandling ()
130
144
}
145
+ return nil
131
146
}
132
147
133
- // StartSignalHandling starts signal handling in a separate goroutine.
134
- func (wd * Watchdog ) StartSignalHandling () {
148
+ // startSignalHandling starts signal handling in a separate goroutine.
149
+ func (wd * Watchdog ) startSignalHandling () {
135
150
sigChan := make (chan os.Signal , 1 )
136
151
// Reset the signal mask before starting of the new loop.
137
152
signal .Reset ()
@@ -157,23 +172,23 @@ func (wd *Watchdog) StartSignalHandling() {
157
172
case sig := <- sigChan :
158
173
switch sig {
159
174
case syscall .SIGINT , syscall .SIGTERM :
160
- wd .stateMutex .Lock ()
161
- if wd .started {
175
+ wd .Instance . stateMutex .Lock ()
176
+ if wd .Instance . started {
162
177
wd .Instance .Stop (30 * time .Second )
163
178
}
164
179
// If we receive one of the "stop" signals, the
165
180
// program should be terminated.
166
- wd .shouldStop = true
167
- wd .stateMutex .Unlock ()
181
+ wd .Instance . shouldStop = true
182
+ wd .Instance . stateMutex .Unlock ()
168
183
case syscall .SIGHUP :
169
184
// Rotate the log files.
170
185
wd .logger .Rotate ()
171
186
default :
172
- wd .stateMutex .Lock ()
173
- if wd .started {
187
+ wd .Instance . stateMutex .Lock ()
188
+ if wd .Instance . started {
174
189
wd .Instance .SendSignal (sig )
175
190
}
176
- wd .stateMutex .Unlock ()
191
+ wd .Instance . stateMutex .Unlock ()
177
192
}
178
193
case _ = <- wd .done :
179
194
return
0 commit comments