github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/common/bwidth.go (about) 1 package common 2 3 import ( 4 "fmt" 5 "net" 6 "sync" 7 "sync/atomic" 8 "time" 9 ) 10 11 var ( 12 bw_mutex sync.Mutex 13 14 dl_last_sec int64 = time.Now().Unix() 15 dl_bytes_so_far int 16 17 DlBytesPrevSec [0x10000]uint64 // this buffer takes 524288 bytes (hope it's not a problem) 18 DlBytesPrevSecIdx uint16 19 20 dl_bytes_priod uint64 21 DlBytesTotal uint64 22 23 upload_limit uint64 24 download_limit uint64 25 26 ul_last_sec int64 = time.Now().Unix() 27 ul_bytes_so_far int 28 29 UlBytesPrevSec [0x10000]uint64 // this buffer takes 524288 bytes (hope it's not a problem) 30 UlBytesPrevSecIdx uint16 31 ul_bytes_priod uint64 32 UlBytesTotal uint64 33 ) 34 35 func TickRecv() (ms int) { 36 tn := time.Now() 37 ms = tn.Nanosecond() / 1e6 38 now := tn.Unix() 39 if now < dl_last_sec { 40 dl_last_sec = now // This is to prevent a lock-up when OS clock is updated back 41 ms = 1e6 - 1 42 } 43 if now != dl_last_sec { 44 for now-dl_last_sec != 1 { 45 DlBytesPrevSec[DlBytesPrevSecIdx] = 0 46 DlBytesPrevSecIdx++ 47 dl_last_sec++ 48 } 49 DlBytesPrevSec[DlBytesPrevSecIdx] = dl_bytes_priod 50 DlBytesPrevSecIdx++ 51 dl_bytes_priod = 0 52 dl_bytes_so_far = 0 53 dl_last_sec = now 54 } 55 return 56 } 57 58 func TickSent() (ms int) { 59 tn := time.Now() 60 ms = tn.Nanosecond() / 1e6 61 now := tn.Unix() 62 if now < ul_last_sec { 63 ul_last_sec = now // This is to prevent a lock-up when OS clock is updated back 64 ms = 1e6 - 1 65 } 66 if now != ul_last_sec { 67 var loop_cnt int 68 for now-ul_last_sec != 1 { 69 UlBytesPrevSec[UlBytesPrevSecIdx] = 0 70 UlBytesPrevSecIdx++ 71 ul_last_sec++ 72 loop_cnt++ 73 } 74 UlBytesPrevSec[UlBytesPrevSecIdx] = ul_bytes_priod 75 UlBytesPrevSecIdx++ 76 ul_bytes_priod = 0 77 ul_bytes_so_far = 0 78 ul_last_sec = now 79 } 80 return 81 } 82 83 // SockRead reads the given number of bytes, but respecting the download limit. 84 // Returns -1 and no error if we can't read any data now, because of bw limit. 85 func SockRead(con net.Conn, buf []byte) (n int, e error) { 86 var toread int 87 bw_mutex.Lock() 88 ms := TickRecv() 89 if DownloadLimit() == 0 { 90 toread = len(buf) 91 } else { 92 toread = ms*int(DownloadLimit())/1000 - dl_bytes_so_far 93 if toread > len(buf) { 94 toread = len(buf) 95 if toread > 4096 { 96 toread = 4096 97 } 98 } else if toread < 0 { 99 toread = 0 100 } 101 } 102 dl_bytes_so_far += toread 103 bw_mutex.Unlock() 104 105 if toread > 0 { 106 // Wait 10 millisecond for a data, timeout if nothing there 107 con.SetReadDeadline(time.Now().Add(10 * time.Millisecond)) 108 n, e = con.Read(buf[:toread]) 109 bw_mutex.Lock() 110 dl_bytes_so_far -= toread 111 if n > 0 { 112 dl_bytes_so_far += n 113 DlBytesTotal += uint64(n) 114 dl_bytes_priod += uint64(n) 115 } 116 bw_mutex.Unlock() 117 } else { 118 n = -1 119 } 120 return 121 } 122 123 // SockWrite sends all the bytes, but respect the upload limit (force delays). 124 // Returns -1 and no error if we can't send any data now, because of bw limit. 125 func SockWrite(con net.Conn, buf []byte) (n int, e error) { 126 var tosend int 127 bw_mutex.Lock() 128 ms := TickSent() 129 if UploadLimit() == 0 { 130 tosend = len(buf) 131 } else { 132 tosend = ms*int(UploadLimit())/1000 - ul_bytes_so_far 133 if tosend > len(buf) { 134 tosend = len(buf) 135 if tosend > 4096 { 136 tosend = 4096 137 } 138 } else if tosend < 0 { 139 tosend = 0 140 } 141 } 142 ul_bytes_so_far += tosend 143 bw_mutex.Unlock() 144 if tosend > 0 { 145 // We used to have SetWriteDeadline() here, but it was causing problems because 146 // in case of a timeout returned "n" was always 0, even if some data got sent. 147 n, e = con.Write(buf[:tosend]) 148 bw_mutex.Lock() 149 ul_bytes_so_far -= tosend 150 if n > 0 { 151 ul_bytes_so_far += n 152 UlBytesTotal += uint64(n) 153 ul_bytes_priod += uint64(n) 154 } 155 bw_mutex.Unlock() 156 } else { 157 n = -1 158 } 159 return 160 } 161 162 func LockBw() { 163 bw_mutex.Lock() 164 } 165 166 func UnlockBw() { 167 bw_mutex.Unlock() 168 } 169 170 func GetAvgBW(arr []uint64, idx uint16, cnt int) uint64 { 171 var sum uint64 172 if cnt <= 0 { 173 return 0 174 } 175 for i := 0; i < cnt; i++ { 176 idx-- 177 sum += arr[idx] 178 } 179 return sum / uint64(cnt) 180 } 181 182 func PrintBWStats() { 183 bw_mutex.Lock() 184 TickRecv() 185 TickSent() 186 fmt.Printf("Downloading at %d/%d KB/s, %s total", 187 GetAvgBW(DlBytesPrevSec[:], DlBytesPrevSecIdx, 5)>>10, DownloadLimit()>>10, BytesToString(DlBytesTotal)) 188 fmt.Printf(" | Uploading at %d/%d KB/s, %s total\n", 189 GetAvgBW(UlBytesPrevSec[:], UlBytesPrevSecIdx, 5)>>10, UploadLimit()>>10, BytesToString(UlBytesTotal)) 190 bw_mutex.Unlock() 191 return 192 } 193 194 func SetDownloadLimit(val uint64) { 195 atomic.StoreUint64(&download_limit, val) 196 } 197 198 func DownloadLimit() uint64 { 199 return atomic.LoadUint64(&download_limit) 200 } 201 202 func SetUploadLimit(val uint64) { 203 atomic.StoreUint64(&upload_limit, val) 204 } 205 206 func UploadLimit() (res uint64) { 207 return atomic.LoadUint64(&upload_limit) 208 }