// vtest flaky: true
// vtest retry: 4
/*
* To verify the effect of "[keep_args_alive]", this attribute may be commented out.
* However it is not guaranteed that then this test will fail.
* To provoke a failure it seems to be best to use `gcc` with optimization:
* `gcc -gc boehm -cc gcc-9 -prod test keep_args_alive_test.c.v`.
* Without optimization, pointer variables may remain on the stack even if
* not used any more.
*/
import rand
import sync

#flag -I@VEXEROOT/vlib/v/slow_tests
#include "keep_args_alive_test_c.h"

fn C.atomic_load_ptr(voidptr) voidptr

fn C.atomic_store_ptr(voidptr, voidptr)

@[keep_args_alive]
fn C.calc_expr_after_delay(voidptr, int, voidptr) int

fn set_vals() voidptr {
	unsafe {
		p := &int(malloc(8000000))
		q := &int(malloc(8000000))
		aa := p + 769345
		*aa = -4578
		bb := q + 572397
		*bb = 793254
		p = &int(0)
		q = &int(0)
		r := &voidptr(malloc(1000000))
		r[456] = aa
		r[7932] = bb
		aa = &int(0)
		bb = &int(0)
		return r
	}
}

fn tt(mut sem sync.Semaphore) int {
	waste_mem(10000, mut sem)
	r := &voidptr(set_vals())
	g := unsafe { C.calc_expr_after_delay(r[456], 12, r[7932]) }
	return g
}

fn waste_mem(n int, mut sem sync.Semaphore) {
	mut m := []voidptr{len: 30}
	for j := 0; n < 0 || j < n; j++ {
		i := rand.intn(30) or { 0 }
		m[i] = unsafe { malloc(10000) }
		fill := rand.intn(256) or { 0 }
		unsafe { C.memset(m[i], fill, 10000) }
		if n < 0 && sem.try_wait() {
			break
		}
	}
}

fn test_keep_args_alive_attribute() {
	mut sem := sync.new_semaphore()
	$if gcboehm ? {
		spawn waste_mem(-1, mut sem)
		spawn waste_mem(-1, mut sem)
		waste_mem(10000, mut sem)
	}
	r := &voidptr(set_vals())
	v := unsafe { C.calc_expr_after_delay(r[456], 12, r[7932]) }
	$if gcboehm ? {
		sem.post()
		sem.post()
	}
	assert v == 738318
}
