added shutdownevent for graceful shutdown
diff --git a/apid.go b/apid.go index 73f243c..7da00d9 100644 --- a/apid.go +++ b/apid.go
@@ -1,9 +1,12 @@ package apid -import "os" +import ( + "os" +) const ( SystemEventsSelector EventSelector = "system event" + ShutdownEventSelector EventSelector = "shutdown event" ) var ( @@ -12,6 +15,8 @@ pluginInitFuncs []PluginInitFunc services Services + + shutdownChan chan int ) type Services interface { @@ -44,6 +49,8 @@ ss.api = s.API() ss.data = s.Data() + shutdownChan = make(chan int) + ss.events.Emit(SystemEventsSelector, APIDInitializedEvent) } @@ -69,6 +76,24 @@ log.Debugf("done initializing plugins") } +func ShutdownPlugins() { + Events().EmitWithCallback(ShutdownEventSelector, ShutdownEvent{"apid is going to shutdown"}, ShutdownHandler) +} + + +func ShutdownHandler(event Event) { + log := Log() + log.Debugf("shutdown apid") + shutdownChan <- 1 +} + +/* wait for the shutdown of registered graceful-shutdown plugins, blocking until the required plugins finish shutdown + * this is used to prevent the main from exiting + */ +func WaitPluginsShutdown() { + <- shutdownChan +} + func AllServices() Services { return services } @@ -124,3 +149,7 @@ type systemEvent struct { description string } + +type ShutdownEvent struct { + Description string +}
diff --git a/events/event_manager.go b/events/event_manager.go index 33b0ee1..12e8f82 100644 --- a/events/event_manager.go +++ b/events/event_manager.go
@@ -13,7 +13,8 @@ func (em *eventManager) Emit(selector apid.EventSelector, event apid.Event) { log.Debugf("emit selector: '%s' event %v: %v", selector, &event, event) - if !em.dispatchers[selector].Send(event) { + eventDispatcher := em.dispatchers[selector] + if (eventDispatcher==nil) || (!eventDispatcher.Send(event)) { em.sendDelivered(selector, event, 0) // in case of no dispatcher } }
diff --git a/events/events_test.go b/events/events_test.go index 70bc19a..b2e3f9d 100644 --- a/events/events_test.go +++ b/events/events_test.go
@@ -6,6 +6,8 @@ . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "sync/atomic" + "fmt" + "strconv" ) var _ = Describe("Events Service", func() { @@ -287,8 +289,75 @@ apid.InitializePlugins() }) + It("shutdown event should be emitted and listened successfully", func(done Done) { + h := func(event apid.Event) { + defer GinkgoRecover() + + if pie, ok := event.(apid.ShutdownEvent); ok { + apid.Events().Close() + Expect(pie.Description).Should(Equal("apid is going to shutdown")) + fmt.Println(pie.Description) + close(done) + } else { + Fail("Received wrong event") + } + } + apid.Events().ListenFunc(apid.ShutdownEventSelector, h) + shutdownHandler := func(event apid.Event) {} + apid.Events().EmitWithCallback(apid.ShutdownEventSelector, apid.ShutdownEvent{"apid is going to shutdown"}, shutdownHandler) + }) + + It("handlers registered by plugins should execute before apid shutdown", func(done Done) { + pluginNum := 10 + count := make([]int, 1) + count[0] = 0 + + // create and register plugins, listen to shutdown event + for i:=0; i<pluginNum; i++ { + apid.RegisterPlugin(createDummyPlugin(i)) + h := func(event apid.Event) { + if pie, ok := event.(apid.ShutdownEvent); ok { + Expect(pie.Description).Should(Equal("apid is going to shutdown")) + count[0] += 1 + } else { + Fail("Received wrong event") + } + } + apid.Events().ListenFunc(apid.ShutdownEventSelector, h) + } + + + apid.InitializePlugins() + + apid.ShutdownPlugins() + + apid.WaitPluginsShutdown() + + defer GinkgoRecover() + apid.Events().Close() + // handlers of all registered plugins have executed + Expect(count[0]).Should(Equal(pluginNum)) + fmt.Println("handlers of all registered plugins have executed") + + close(done) + }) }) +func createDummyPlugin(id int) apid.PluginInitFunc{ + xData := make(map[string]interface{}) + xData["schemaVersion"] = "1.2.3" + p := func(s apid.Services) (pd apid.PluginData, err error) { + pd = apid.PluginData{ + Name: "test plugin " + strconv.Itoa(id), + Version: "1.0.0", + ExtraData: xData, + } + return + } + return p + +} + type test_handler struct { description string f func(event apid.Event)