Skip to content

Commit 47f49ae

Browse files
committed
Introducing StartChildBySpec/StopChildByName to dynamically add and remove child specifications.
1 parent 093c5d1 commit 47f49ae

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed

gen/supervisor.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ type messageStartChild struct {
107107
args []etf.Term
108108
}
109109

110+
type messageStartChildBySpec struct {
111+
childSpec SupervisorChildSpec
112+
}
113+
114+
type messageStopChildByName struct {
115+
processName string
116+
}
117+
110118
// ProcessInit
111119
func (sv *Supervisor) ProcessInit(p Process, args ...etf.Term) (ProcessState, error) {
112120
behavior, ok := p.Behavior().(SupervisorBehavior)
@@ -182,6 +190,45 @@ func (sv *Supervisor) StartChild(supervisor Process, name string, args ...etf.Te
182190
return process, nil
183191
}
184192

193+
// StartChildBySpec dynamically adds child spec and starts a child process with given spec which is undefined by Init call.
194+
// not surport simple_one_for_one strategy type.
195+
// Similar to erlang[supervisor:start_child/2].
196+
func StartChildBySpec(supervisor Process, spec SupervisorChildSpec) (Process, error) {
197+
if spec.state != supervisorChildStateStart {
198+
return nil, fmt.Errorf("spec state is invalid %#v", spec.state)
199+
}
200+
message := messageStartChildBySpec{
201+
childSpec: spec,
202+
}
203+
value, err := supervisor.Direct(message)
204+
if err != nil {
205+
return nil, err
206+
}
207+
process, ok := value.(Process)
208+
if !ok {
209+
return nil, fmt.Errorf("internal error: can't start child %#v", value)
210+
}
211+
return process, nil
212+
}
213+
214+
// StopChildByName dynamically deletes child spec and stop a child process with given process name.
215+
// not surport simple_one_for_one strategy type.
216+
// Similar to erlang[supervisor:terminate_child(Sup, ID), supervisor:delete_child(Sup, ID)].
217+
func StopChildByName(supervisor Process, processName string) (SupervisorChildSpec, error) {
218+
message := messageStopChildByName{
219+
processName: processName,
220+
}
221+
value, err := supervisor.Direct(message)
222+
if err != nil {
223+
return SupervisorChildSpec{}, err
224+
}
225+
childSpec, ok := value.(SupervisorChildSpec)
226+
if !ok {
227+
return SupervisorChildSpec{}, fmt.Errorf("internal error:%#v", value)
228+
}
229+
return childSpec, nil
230+
}
231+
185232
func startChildren(supervisor Process, spec *SupervisorSpec) {
186233
spec.restarts = append(spec.restarts, time.Now().Unix())
187234
if len(spec.restarts) > int(spec.Strategy.Intensity) {
@@ -261,6 +308,42 @@ func handleDirect(supervisor Process, spec *SupervisorSpec, message interface{})
261308
spec.Children = append(spec.Children, childSpec)
262309
return process, nil
263310

311+
case messageStartChildBySpec:
312+
if spec.Strategy.Type == SupervisorStrategySimpleOneForOne {
313+
return nil, fmt.Errorf("startSpecChild not surport simple_one_for_one")
314+
}
315+
childSpec := m.childSpec
316+
_, err := lookupSpecByName(childSpec.Name, spec.Children)
317+
if err == nil {
318+
return nil, fmt.Errorf("%s specChild is exist", childSpec.Name)
319+
}
320+
childSpec.state = supervisorChildStateRunning
321+
process := startChild(supervisor, childSpec.Name, childSpec.Child, childSpec.Options, childSpec.Args...)
322+
childSpec.process = process
323+
spec.Children = append(spec.Children, childSpec)
324+
return process, nil
325+
326+
case messageStopChildByName:
327+
if spec.Strategy.Type == SupervisorStrategySimpleOneForOne {
328+
return nil, fmt.Errorf("startSpecChild not surport simple_one_for_one")
329+
}
330+
processName := m.processName
331+
childSpec, err := lookupSpecByName(processName, spec.Children)
332+
if err != nil {
333+
return nil, err
334+
}
335+
childProc := childSpec.process
336+
if childSpec.state != supervisorChildStateRunning && childProc != nil {
337+
return nil, fmt.Errorf("childSpec:%v can not stop", childSpec)
338+
}
339+
//delete spec
340+
supervisor.Unlink(childProc.Self())
341+
spec.Children = deleteSpecByName(processName, spec.Children)
342+
//terminate child
343+
//Similar to erlang[supervisor:shutdown/1](exit(Pid, shutdown)).
344+
childProc.Exit("shutdown")
345+
return childSpec, nil
346+
264347
default:
265348
}
266349

@@ -446,3 +529,13 @@ func lookupSpecByName(specName string, spec []SupervisorChildSpec) (SupervisorCh
446529
}
447530
return SupervisorChildSpec{}, fmt.Errorf("unknown child")
448531
}
532+
533+
func deleteSpecByName(specName string, spec []SupervisorChildSpec) []SupervisorChildSpec {
534+
ret := make([]SupervisorChildSpec, 0, len(spec))
535+
for i := range spec {
536+
if spec[i].Name != specName {
537+
ret = append(ret, spec[i])
538+
}
539+
}
540+
return ret
541+
}

0 commit comments

Comments
 (0)