96 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			96 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
| // 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
 | |
| import os
 | |
| 
 | |
| // Unsafe pointer
 | |
| type Pointer voidptr
 | |
| 
 | |
| // Mutex HANDLE
 | |
| type MHANDLE voidptr
 | |
| 
 | |
| struct Mutex {
 | |
| mut:
 | |
|     mx           MHANDLE // mutex handle
 | |
|     wstate       u32     // wait 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 
 | |
| }
 | |
| 
 | |
| 
 | |
| const (
 | |
|     WAIT   = u32(8)  // Waiting mutex
 | |
|     WOKEN  = u32(16) // Woken mutex
 | |
|     ABOND  = u32(32)
 | |
|     BROKEN = u32(64)
 | |
|     DESTROYED = u32(0)
 | |
| )
 | |
| 
 | |
| const (
 | |
|     INFINITY = 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 (m mut Mutex) lock() {
 | |
|     // if mutex handle not initalized
 | |
|     if m.mx == MHANDLE(0) {
 | |
|         m.mx = C.CreateMutex(0, false, 0)
 | |
|         _pmhx := int(m.mx)
 | |
|         if (((_pmhx & 0xff) - 1) == 0) || (_pmhx == os.INVALID_HANDLE_VALUE) {
 | |
|             m.wstate = BROKEN // handle broken and mutex state are broken
 | |
|             return
 | |
|         }
 | |
|     }
 | |
|     state := C.WaitForSingleObject(m.mx, INFINITY) // infinity wait
 | |
|     // for {
 | |
|     //     if (m.cycle_woken - 1) < 0 {
 | |
|     //         break
 | |
|     //     }
 | |
|     //     if state&0x00000080 {
 | |
|     //         continue // abondoned
 | |
|     //     }
 | |
|     //     m.cycle_wait++
 | |
|     // }
 | |
|     match state {
 | |
|         WAIT_FAILED    => m.wstate = BROKEN
 | |
|         WAIT_ABANDONED => m.wstate = ABOND
 | |
|         WAIT_OBJECT_0  => m.wstate = WAIT & u32(0xff)
 | |
|     }
 | |
|     // todo implement atomic counter
 | |
| }
 | |
| 
 | |
| pub fn (m mut Mutex) unlock() {
 | |
|     _pmx := &m.mx
 | |
|     if _pmx != os.INVALID_HANDLE_VALUE {
 | |
|         if m.wstate == (WAIT & u32(0xff)) {
 | |
|             if C.ReleaseMutex(_pmx) != 0 {
 | |
|                 m.wstate = WOKEN // woken up mutex
 | |
|                 return
 | |
|             }
 | |
|             m.wstate = ABOND
 | |
|             return
 | |
|         }
 | |
|     }
 | |
|     m.wstate = BROKEN
 | |
| }
 | |
| 
 | |
| pub fn (m mut Mutex) destroy() {
 | |
|     if m.wstate == WAIT {
 | |
|         m.unlock() // unlock mutex before destroying
 | |
|     }
 | |
|     m.wstate = DESTROYED // setting up reference to invalid state
 | |
|     C.CloseHandle(m.mx) // destroy mutex
 | |
| }
 |