import sync
import time

struct Counter {
pub mut:
	i int
}

fn write_10000(mut co Counter, mut mx sync.RwMutex) {
	mx.@lock()
	co.i = 10000
	mx.unlock()
}

fn test_rwmutex() {
	mut co := &Counter{10086}
	mut mx := sync.new_rwmutex()
	mx.@lock()
	co.i = 888
	th1 := spawn write_10000(mut co, mut mx)
	mx.unlock() // after mx unlock, thread write_10000 can continue
	th1.wait()
	assert co.i == 10000

	mx.@rlock()
	th2 := spawn write_10000(mut co, mut mx) // write_10000 will be blocked
	co.i = 999 // for demo purpose, don't modify data in rlock!
	time.sleep(1 * time.millisecond)
	assert co.i == 999
	mx.runlock() // after rlock released, write_10000 can continue
	th2.wait()
	assert co.i == 10000
	mx.destroy()
}

fn test_try_lock_rwmutex() {
	// In Windows, try_lock only avalible after Windows 7
	$if windows {
		$if !windows_7 ? {
			return
		}
	}
	mut mx := sync.new_rwmutex()

	// try_rlock will always fail when mx locked
	mx.@lock()
	try_fail_reading1 := mx.try_rlock()
	try_fail_writing1 := mx.try_wlock()
	assert try_fail_reading1 == false
	assert try_fail_writing1 == false

	mx.unlock()

	// try_rlock will always succeed when mx unlocked,
	// multiple try_rlock can apply to the same mx
	try_success_reading2 := mx.try_rlock()
	try_success_reading3 := mx.try_rlock()
	assert try_success_reading2 == true
	assert try_success_reading3 == true

	// if mx is rlocked, then the try_wlock will fail
	try_fail_writing2 := mx.try_wlock()
	assert try_fail_writing2 == false

	mx.runlock()
	mx.runlock() // you must release rlock mutiple times, as it was rlocked multiple times

	// after mx release all rlock, try_wlock will succeed
	try_success_writing3 := mx.try_wlock()
	assert try_success_writing3 == true

	mx.unlock() // you must unlock it, after try_wlock success
	mx.destroy()
}
