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