diff options
author | Matthew Holt <mholt@users.noreply.github.com> | 2022-09-19 16:20:58 -0600 |
---|---|---|
committer | Matthew Holt <mholt@users.noreply.github.com> | 2022-09-19 16:20:58 -0600 |
commit | 0950ba4f0b77f2a9134188386345fccdfddb80ad (patch) | |
tree | b6d3dadd8652c3159db8cbad10e417d0a6c55deb /modules/caddyevents | |
parent | c7a6bc59345ca4075cc8bf97bd80900d28d8e05d (diff) |
events: Make event data exported
This could lead to bugs if handlers are not careful, but it is surely
useful. We'll see how it goes, what the feedback is like, etc.
Diffstat (limited to 'modules/caddyevents')
-rw-r--r-- | modules/caddyevents/app.go | 39 |
1 files changed, 28 insertions, 11 deletions
diff --git a/modules/caddyevents/app.go b/modules/caddyevents/app.go index db2192c..6b10460 100644 --- a/modules/caddyevents/app.go +++ b/modules/caddyevents/app.go @@ -201,6 +201,9 @@ func (app *App) On(eventName string, handler Handler) error { // Emit creates and dispatches an event named eventName to all relevant handlers with // the metadata data. Events are emitted and propagated synchronously. The returned Event // value will have any additional information from the invoked handlers. +// +// Note that the data map is not copied, for efficiency. After Emit() is called, the +// data passed in should not be changed in other goroutines. func (app *App) Emit(ctx caddy.Context, eventName string, data map[string]any) Event { logger := app.logger.With(zap.String("name", eventName)) @@ -212,11 +215,11 @@ func (app *App) Emit(ctx caddy.Context, eventName string, data map[string]any) E eventName = strings.ToLower(eventName) e := Event{ + Data: data, id: id, ts: time.Now(), name: eventName, origin: ctx.Module(), - data: data, } logger = logger.With( @@ -244,12 +247,12 @@ func (app *App) Emit(ctx caddy.Context, eventName string, data map[string]any) E case "event.module": return e.origin.CaddyModule().ID, true case "event.data": - return e.data, true + return e.Data, true } if strings.HasPrefix(key, "event.data.") { key = strings.TrimPrefix(key, "event.data.") - if val, ok := data[key]; ok { + if val, ok := e.Data[key]; ok { return val, true } } @@ -257,7 +260,7 @@ func (app *App) Emit(ctx caddy.Context, eventName string, data map[string]any) E return nil, false }) - logger.Debug("event", zap.Any("data", e.data)) + logger.Debug("event", zap.Any("data", e.Data)) // invoke handlers bound to the event by name and also all events; this for loop // iterates twice at most: once for the event name, once for "" (all events) @@ -314,26 +317,40 @@ func (app *App) Emit(ctx caddy.Context, eventName string, data map[string]any) E } // Event represents something that has happened or is happening. +// An Event value is not synchronized, so it should be copied if +// being used in goroutines. +// +// EXPERIMENTAL: As with the rest of this package, events are +// subject to change. type Event struct { - id uuid.UUID - ts time.Time - name string - origin caddy.Module - data map[string]any - // If non-nil, the event has been aborted, meaning // propagation has stopped to other handlers and // the code should stop what it was doing. Emitters // may choose to use this as a signal to adjust their // code path appropriately. Aborted error + + // The data associated with the event. Usually the + // original emitter will be the only one to set or + // change these values, but the field is exported + // so handlers can have full access if needed. + // However, this map is not synchronized, so + // handlers must not use this map directly in new + // goroutines; instead, copy the map to use it in a + // goroutine. + Data map[string]any + + id uuid.UUID + ts time.Time + name string + origin caddy.Module } // CloudEvent exports event e as a structure that, when // serialized as JSON, is compatible with the // CloudEvents spec. func (e Event) CloudEvent() CloudEvent { - dataJSON, _ := json.Marshal(e.data) + dataJSON, _ := json.Marshal(e.Data) return CloudEvent{ ID: e.id.String(), Source: e.origin.CaddyModule().String(), |