95 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			95 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			V
		
	
	
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
 | 
						|
// Use of this source code is governed by an MIT license
 | 
						|
// that can be found in the LICENSE file.
 | 
						|
 | 
						|
module sync
 | 
						|
 | 
						|
// Mutex HANDLE
 | 
						|
type MHANDLE voidptr
 | 
						|
 | 
						|
//[init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
 | 
						|
pub struct Mutex {
 | 
						|
mut:
 | 
						|
	mx           MHANDLE    // mutex handle
 | 
						|
	state        MutexState // mutex state
 | 
						|
	cycle_wait   i64        // waiting cycles (implemented only with atomic)
 | 
						|
	cycle_woken  i64        // woken cycles    ^
 | 
						|
	reader_sem   u32        // reader semarphone
 | 
						|
	writer_sem   u32        // writer semaphones
 | 
						|
}
 | 
						|
 | 
						|
enum MutexState {
 | 
						|
	broken
 | 
						|
	waiting
 | 
						|
	released
 | 
						|
	abandoned
 | 
						|
	destroyed
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	INFINITE = 0xffffffff
 | 
						|
)
 | 
						|
 | 
						|
// Ref - https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject#return-value
 | 
						|
const (
 | 
						|
	WAIT_ABANDONED     = 0x00000080
 | 
						|
	WAIT_IO_COMPLETION = 0x000000C0
 | 
						|
	WAIT_OBJECT_0      = 0x00000000
 | 
						|
	WAIT_TIMEOUT       = 0x00000102
 | 
						|
	WAIT_FAILED        = 0xFFFFFFFF
 | 
						|
)
 | 
						|
 | 
						|
pub fn new_mutex() Mutex {
 | 
						|
	sm := Mutex{}
 | 
						|
	unsafe {
 | 
						|
		mut m := sm		
 | 
						|
		m.mx = C.CreateMutex(0, false, 0)
 | 
						|
		if isnil(m.mx) {
 | 
						|
			m.state = .broken // handle broken and mutex state are broken
 | 
						|
			return sm
 | 
						|
		}		
 | 
						|
		return sm
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
pub fn (m mut Mutex) lock() {
 | 
						|
	// if mutex handle not initalized
 | 
						|
	if isnil(m.mx) {
 | 
						|
		m.mx = C.CreateMutex(0, false, 0)
 | 
						|
		if isnil(m.mx) {
 | 
						|
			m.state = .broken // handle broken and mutex state are broken
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
	state := C.WaitForSingleObject(m.mx, INFINITE) // infinite wait
 | 
						|
	/* TODO fix match/enum combo
 | 
						|
	m.state = match state {
 | 
						|
		WAIT_ABANDONED { .abandoned }
 | 
						|
		WAIT_OBJECT_0  { .waiting }
 | 
						|
		else           { .broken }
 | 
						|
	}
 | 
						|
	*/
 | 
						|
	mut s := MutexState.broken
 | 
						|
	if state == WAIT_ABANDONED { s = .abandoned }
 | 
						|
	else if state == WAIT_OBJECT_0 { s = .waiting }
 | 
						|
	m.state = s
 | 
						|
}
 | 
						|
 | 
						|
pub fn (m mut Mutex) unlock() {
 | 
						|
	if m.state == .waiting {
 | 
						|
		if ReleaseMutex(m.mx) {
 | 
						|
			m.state = .broken
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
	m.state = .released
 | 
						|
}
 | 
						|
 | 
						|
pub fn (m mut Mutex) destroy() {
 | 
						|
	if m.state == .waiting {
 | 
						|
		m.unlock() // unlock mutex before destroying
 | 
						|
	}
 | 
						|
	C.CloseHandle(m.mx)  // destroy mutex
 | 
						|
	m.state = .destroyed // setting up reference to invalid state
 | 
						|
}
 |