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 }