github.com/v2fly/tools@v0.100.0/internal/event/core/export.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package core
     6  
     7  import (
     8  	"context"
     9  	"sync/atomic"
    10  	"time"
    11  	"unsafe"
    12  
    13  	"github.com/v2fly/tools/internal/event/label"
    14  )
    15  
    16  // Exporter is a function that handles events.
    17  // It may return a modified context and event.
    18  type Exporter func(context.Context, Event, label.Map) context.Context
    19  
    20  var (
    21  	exporter unsafe.Pointer
    22  )
    23  
    24  // SetExporter sets the global exporter function that handles all events.
    25  // The exporter is called synchronously from the event call site, so it should
    26  // return quickly so as not to hold up user code.
    27  func SetExporter(e Exporter) {
    28  	p := unsafe.Pointer(&e)
    29  	if e == nil {
    30  		// &e is always valid, and so p is always valid, but for the early abort
    31  		// of ProcessEvent to be efficient it needs to make the nil check on the
    32  		// pointer without having to dereference it, so we make the nil function
    33  		// also a nil pointer
    34  		p = nil
    35  	}
    36  	atomic.StorePointer(&exporter, p)
    37  }
    38  
    39  // deliver is called to deliver an event to the supplied exporter.
    40  // it will fill in the time.
    41  func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context {
    42  	// add the current time to the event
    43  	ev.at = time.Now()
    44  	// hand the event off to the current exporter
    45  	return exporter(ctx, ev, ev)
    46  }
    47  
    48  // Export is called to deliver an event to the global exporter if set.
    49  func Export(ctx context.Context, ev Event) context.Context {
    50  	// get the global exporter and abort early if there is not one
    51  	exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
    52  	if exporterPtr == nil {
    53  		return ctx
    54  	}
    55  	return deliver(ctx, *exporterPtr, ev)
    56  }
    57  
    58  // ExportPair is called to deliver a start event to the supplied exporter.
    59  // It also returns a function that will deliver the end event to the same
    60  // exporter.
    61  // It will fill in the time.
    62  func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) {
    63  	// get the global exporter and abort early if there is not one
    64  	exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
    65  	if exporterPtr == nil {
    66  		return ctx, func() {}
    67  	}
    68  	ctx = deliver(ctx, *exporterPtr, begin)
    69  	return ctx, func() { deliver(ctx, *exporterPtr, end) }
    70  }