How can I guarantee that a function will take some time in Go?

I am using EnScrypt for the SQRL client in Go. The function must be run until it uses the minimum amount of processor time. My Python code is as follows:

def enscrypt_time(salt, password, seconds, n=9, r=256): N = 1 << n start = time.process_time() end = start + seconds data = acc = scrypt.hash(password, salt, N, r, 1, 32) i = 1 while time.process_time() < end: data = scrypt.hash(password, data, N, r, 1, 32) acc = xor_bytes(acc, data) i += 1 return i, time.process_time() - start, acc 

Converting this to Go is pretty simple, with the exception of the process_time function. I cannot use time.Time / Timer because they measure the wall clock time (which is affected by everything else that can work on the system). I need the actual use of CPU time, ideally, by a function, or at least by the thread or process in which it is running.

What is the equivalent of Go process_time ?

https://docs.python.org/3/library/time.html#time.process_time

+5
source share
1 answer

You can use runtime.LockOSThread() to associate the calling goroutine with the current OS thread. This ensures that no other goroutines are assigned to this thread, so your goroutine will work, not be interrupted or delayed. No other goroutines will interfere when the thread is blocked.

After that, you just need a loop until these seconds have passed. You must call runtime.UnlockOSThread() to "free" the thread and make it available to other goroutines to execute, it is best done as a defer .

See this example:

 func runUntil(end time.Time) { runtime.LockOSThread() defer runtime.UnlockOSThread() for time.Now().Before(end) { } } 

To wait 2 seconds, it might look like this:

 start := time.Now() end := start.Add(time.Second * 2) runUntil(end) fmt.Println("Verify:", time.Now().Sub(start)) 

It is printed, for example:

 Verify: 2.0004556s 

Of course, you can specify less than a second, for example. to wait 100 ms:

 start := time.Now() runUntil(start.Add(time.Millisecond * 100)) fmt.Println("Verify:", time.Now().Sub(start)) 

Output:

 Verify: 100.1278ms 

You can use a different version of this function if it suits you, which takes time to "wait" as the value of time.Duration :

 func wait(d time.Duration) { runtime.LockOSThread() defer runtime.UnlockOSThread() for end := time.Now().Add(d); time.Now().Before(end); { } } 

Using this:

 start = time.Now() wait(time.Millisecond * 200) fmt.Println("Verify:", time.Now().Sub(start)) 

Output:

 Verify: 200.1546ms 

Note. . Please note that cycles in the above functions will use the CPU ruthlessly, since they do not have sleep or I / O locks, they will simply query the current system time and compare it with the deadline.

What should I do if an attacker increases the system load by several simultaneous attempts?

Go runtime limits the system threads that goroutines can execute at the same time. This is controlled by runtime.GOMAXPROCS() , so this is already a limitation. By default, the number of available processor cores is used, and you can change it at any time. This also creates a bottleneck though, using runtime.LockOSThread() if the number of blocked threads is GOMAXPROCS at any given time, which blocks the execution of other goroutines until the thread is unlocked.

See related questions:

The number of threads used when starting Go

Why does this not create many threads when many goroutines are locked in a file entry in golang?

+3
source

Source: https://habr.com/ru/post/1258266/


All Articles