' Classes/System/Threading/WaitHandle.ab

#ifndef __SYSTEM_THREADING_WAITHANDLE_AB__
#define __SYSTEM_THREADING_WAITHANDLE_AB__

#require <Classes/System/misc.ab>

Namespace System
Namespace Threading

Namespace Detail
	TypeDef PFNSignalObjectAndWait = *Function(hObjectToSignal As HANDLE, hObjectToWaitOn As HANDLE, dwMilliseconds As DWord, bAlertable As DWord) As DWord
End Namespace

Class WaitHandle
	Implements System.IDisposable
Public
	' Properties
'	Const Function SafeWaitHandle() As SafeWaitHandle
'	Sub SafeWaitHandle(h As SafeWaitHandle)

	Const Virtual Function Handle() As HANDLE
		Return h
	End Function

	Virtual Sub Handle(newHandle As HANDLE)
		h = newHandle
	End Sub

	' Methods
	Virtual Sub WaitHandle()
	End Sub

	Virtual Sub ~WaitHandle()
		This.Dispose()
	End Sub

	Virtual Sub Close()
		This.Dispose()
	End Sub

	Virtual Sub Dispose()
		Dim hDisposing = InterlockedExchangePointer(h, 0)
		If hDisposing <> 0 Then
			CloseHandle(hDisposing)
		End If
	End Sub

	Function WaitOne() As Boolean
		Return WaitOne(INFINITE, FALSE)
	End Function

	Function WaitOne(millisecondsTimeout As Long, exitContext As Boolean) As Boolean
		Return WaitHandle.AfterWait(WaitForSingleObject(h, millisecondsTimeout As DWord), 1)
	End Function

	Function WaitOne(timeout As System.TimeSpan, exitContext As Boolean) As Boolean
		Return WaitHandle.AfterWait(WaitForSingleObject(h, timeout.TotalMilliseconds() As DWord), 1)
	End Function

	Static Function WaitAll(count As DWord, handles As *HANDLE) As Boolean
		Return WaitAll(count, handles, INFINITE, FALSE)
	End Function

	Static Function WaitAll(count As DWord, handles As *HANDLE, millisecondsTimeout As Long, exitContext As Boolean) As Boolean
		Return WaitHandle.AfterWait(WaitForMultipleObjects(count, handles, TRUE, millisecondsTimeout), count)
	End Function

	Static Function WaitAll(count As DWord, handles As *HANDLE, timeout As System.TimeSpan, exitContext As Boolean) As Boolean
		Return WaitHandle.AfterWait(WaitForMultipleObjects(count, handles, TRUE, timeout.TotalMilliseconds() As DWord), count)
	End Function

	Static Function WaitAny(count As DWord, handles As *HANDLE) As Boolean
		Return WaitAny(count, handles, INFINITE, FALSE)
	End Function

	Static Function WaitAny(count As DWord, handles As *HANDLE, millisecondsTimeout As Long, exitContext As Boolean) As Boolean
		Return WaitHandle.AfterWait(WaitForMultipleObjects(count, handles, FALSE, millisecondsTimeout), count)
	End Function

	Static Function WaitAny(count As DWord, handles As *HANDLE, timeout As System.TimeSpan, exitContext As Boolean) As Boolean
		Return WaitHandle.AfterWait(WaitForMultipleObjects(count, handles, FALSE, timeout.TotalMilliseconds() As DWord), count)
	End Function

	Static Function SignalAndWait(toSignal As WaitHandle, toWaitOn As WaitHandle) As Boolean
		Return SignalAndWait(toSignal, toWaitOn, INFINITE, FALSE)
	End Function

	Static Function SignalAndWait (toSignal As WaitHandle, toWaitOn As WaitHandle, timeout As System.TimeSpan, exitContext As Boolean) As Boolean
		Return SignalAndWait(toSignal, toWaitOn, timeout.TotalMilliseconds() As Long, FALSE)
	End Function

	Static Function SignalAndWait(toSignal As WaitHandle, toWaitOn As WaitHandle, millisecondsTimeout As Long, exitContext As Boolean) As Boolean
		Dim pSignalObjectAndWait = GetProcAddress(GetModuleHandle("Kernel32.dll"), "SignalObjectAndWait") As Detail.PFNSignalObjectAndWait
		If pSignalObjectAndWait = 0 Then
			Throw New PlatformNotSupportedException("WaitHandle.SignalAndWait: This platform doesn't supoort this operation.")
		End If
		Return WaitHandle.AfterWait(pSignalObjectAndWait(toSignal.Handle, toWaitOn.Handle, millisecondsTimeout As DWord, FALSE), 1)
	End Function

Public
	Function WaitTimeout() As Long
		Return WAIT_TIMEOUT
	End Function

Protected
	Static Const InvalidHandle = INVALID_HANDLE_VALUE As HANDLE

Private
	h As HANDLE

	Static Function AfterWait(ret As DWord, n As DWord) As Boolean
		Select Case ret
			Case WAIT_TIMEOUT
				Return False
			Case WAIT_ABANDONED
				' Throw AbandonedMutexException
				Debug
				ExitThread(0)
			'Case WAIT_FAILED
			Case Else
				If WAIT_OBJECT_0 <= ret And ret < WAIT_OBJECT_0 + n Then
					Return True
				End If
				' ObjectDisposedException?
				Debug
				ExitThread(0)
		End Select
	End Function
End Class

End Namespace 'Threading
End Namespace 'System

#endif '__SYSTEM_THREADING_WAITHANDLE_AB__
