github.com/sttk/sabi@v0.5.0/notify.go (about)

     1  // Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved.
     2  // This program is free software under MIT License.
     3  // See the file LICENSE in this distribution for more details.
     4  
     5  package sabi
     6  
     7  import (
     8  	"path/filepath"
     9  	"runtime"
    10  	"sync"
    11  	"time"
    12  )
    13  
    14  // ErrOccasion is a struct which contains time and posision in a source file
    15  // when an Err occured.
    16  type ErrOccasion struct {
    17  	time time.Time
    18  	file string
    19  	line int
    20  }
    21  
    22  // Time is a method which returns time when this Err occured.
    23  func (e ErrOccasion) Time() time.Time {
    24  	return e.time
    25  }
    26  
    27  // File is a method which returns the file name where this Err occured.
    28  func (e ErrOccasion) File() string {
    29  	return e.file
    30  }
    31  
    32  // Line is a method which returns the line number where this Err occured.
    33  func (e ErrOccasion) Line() int {
    34  	return e.line
    35  }
    36  
    37  type handlerListElem struct {
    38  	handler func(Err, ErrOccasion)
    39  	next    *handlerListElem
    40  }
    41  
    42  type handlerList struct {
    43  	head *handlerListElem
    44  	last *handlerListElem
    45  }
    46  
    47  var (
    48  	syncErrHandlers  = handlerList{nil, nil}
    49  	asyncErrHandlers = handlerList{nil, nil}
    50  	isErrCfgsFixed   = false
    51  	errCfgMutex      = sync.Mutex{}
    52  )
    53  
    54  // Adds an Err creation event handler which is executed synchronously.
    55  // Handlers added with this method are executed in the order of addition.
    56  func AddSyncErrHandler(handler func(Err, ErrOccasion)) {
    57  	errCfgMutex.Lock()
    58  	defer errCfgMutex.Unlock()
    59  
    60  	if isErrCfgsFixed {
    61  		return
    62  	}
    63  
    64  	last := syncErrHandlers.last
    65  	syncErrHandlers.last = &handlerListElem{handler, nil}
    66  
    67  	if last != nil {
    68  		last.next = syncErrHandlers.last
    69  	}
    70  
    71  	if syncErrHandlers.head == nil {
    72  		syncErrHandlers.head = syncErrHandlers.last
    73  	}
    74  }
    75  
    76  // Adds a Err creation event handlers which is executed asynchronously.
    77  func AddAsyncErrHandler(handler func(Err, ErrOccasion)) {
    78  	errCfgMutex.Lock()
    79  	defer errCfgMutex.Unlock()
    80  
    81  	if isErrCfgsFixed {
    82  		return
    83  	}
    84  
    85  	last := asyncErrHandlers.last
    86  	asyncErrHandlers.last = &handlerListElem{handler, nil}
    87  
    88  	if last != nil {
    89  		last.next = asyncErrHandlers.last
    90  	}
    91  
    92  	if asyncErrHandlers.head == nil {
    93  		asyncErrHandlers.head = asyncErrHandlers.last
    94  	}
    95  }
    96  
    97  // Fixes configuration for Err creation event handlers.
    98  // After calling this function, handlers cannot be registered any more and the
    99  // notification becomes effective.
   100  func FixErrCfgs() {
   101  	isErrCfgsFixed = true
   102  }
   103  
   104  func notifyErr(err Err) {
   105  	if !isErrCfgsFixed {
   106  		return
   107  	}
   108  
   109  	if syncErrHandlers.head == nil && asyncErrHandlers.head == nil {
   110  		return
   111  	}
   112  
   113  	var occ ErrOccasion
   114  	occ.time = time.Now()
   115  
   116  	_, file, line, ok := runtime.Caller(2)
   117  	if ok {
   118  		occ.file = filepath.Base(file)
   119  		occ.line = line
   120  	}
   121  
   122  	for el := syncErrHandlers.head; el != nil; el = el.next {
   123  		el.handler(err, occ)
   124  	}
   125  
   126  	if asyncErrHandlers.head != nil {
   127  		for el := asyncErrHandlers.head; el != nil; el = el.next {
   128  			go el.handler(err, occ)
   129  		}
   130  	}
   131  }