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  }