' Classes/System/Threading/WaitHandle.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
	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 As DWord)
	End Function

	Function WaitOne(millisecondsTimeout As DWord) As Boolean
		If h = 0 Then
			Throw New ObjectDisposedException("WaitHandle.WaitOne")
		End If
		Return afterWait(WaitForSingleObject(h, millisecondsTimeout As DWord), 1)
	End Function

	Function WaitOne(millisecondsTimeout As Long) As Boolean
		ThrowIfInvalidLongValue(millisecondsTimeout)
		Return WaitOne(millisecondsTimeout As DWord)
	End Function

	Function WaitOne(timeout As System.TimeSpan) As Boolean
		Return WaitOne(timeout.TotalMilliseconds() As Long)
	End Function

	Static Function WaitAll(handles As Collections.Generic.IList<WaitHandle>) As Boolean
		Return waitImpl(handles, TRUE, INFINITE As DWord)
	End Function

	Static Function WaitAll(handles As Collections.Generic.IList<WaitHandle>, millisecondsTimeout As Long) As Boolean
		ThrowIfInvalidLongValue(millisecondsTimeout)
		Return waitImpl(handles, TRUE, millisecondsTimeout As DWord)
	End Function

	Static Function WaitAll(handles As Collections.Generic.IList<WaitHandle>, millisecondsTimeout As DWord) As Boolean
		Return waitImpl(handles, TRUE, millisecondsTimeout)
	End Function

	Static Function WaitAll(handles As Collections.Generic.IList<WaitHandle>, timeout As System.TimeSpan) As Boolean
		Return WaitAny(handles, timeout.TotalMilliseconds() As Long)
	End Function

	Static Function WaitAny(handles As Collections.Generic.IList<WaitHandle>) As Boolean
		Return waitImpl(handles, FALSE, INFINITE As DWord)
	End Function

	Static Function WaitAny(handles As Collections.Generic.IList<WaitHandle>, millisecondsTimeout As Long) As Boolean
		ThrowIfInvalidLongValue(millisecondsTimeout)
		Return waitImpl(handles, FALSE, millisecondsTimeout As DWord)
	End Function

	Static Function WaitAny(handles As Collections.Generic.IList<WaitHandle>, millisecondsTimeout As DWord) As Boolean
		Return waitImpl(handles, FALSE, millisecondsTimeout)
	End Function

	Static Function WaitAny(handles As Collections.Generic.IList<WaitHandle>, timeout As System.TimeSpan) As Boolean
		Return WaitAny(handles, timeout.TotalMilliseconds() As Long)
	End Function

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

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

	Static Function SignalAndWait(toSignal As WaitHandle, toWaitOn As WaitHandle, timeout As Long) As Boolean
		Return SignalAndWait(toSignal, toWaitOn, timeout As DWord)
	End Function

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

	Static Const WaitTimeout = WAIT_TIMEOUT

Protected
	Static Const InvalidHandle = INVALID_HANDLE_VALUE As HANDLE

Private
	h As HANDLE

	/*!
	@brief WaitAll/WaitAnyの実装
	@param[in] handles 待機ハンドル
	@param[in] waitAll WaitAllならTRUE、WaitAnyならFALSE
	@param[in] millisecondsTimeout タイムアウトまでの時間。-1 As DWordなら無限に待ち続ける。
	@return シグナルが発生したらTrue、タイムアウトになったらFalse。
	@date 2008/08/27
	@auther Egtra
	*/
	Static Function waitImpl(handles As Collections.Generic.IList<WaitHandle>, waitAll As BOOL, millisecondsTimeout As DWord) As Boolean
'		If ActiveBasic.IsNothing(handles) Then
'			Throw New ArgumentNullException("handles")
'		End If
		Dim count = handles.Count
		If count > MAXIMUM_WAIT_OBJECTS Then
			Throw New InvalidOperationException("handles.Count > MAXIMUM_WAIT_OBJECTS")
		End If
		Return afterWait(WaitForMultipleObjects(count, ToArrayOfHANDLE(handles), waitAll, millisecondsTimeout), count)
	End Function

	/*!
	@brief 待機が終了した後の処理を行う。
	@param[in] ret 待機関数の戻り値
	@param[in] n 待機させたハンドルの数
	@return シグナルが発生したらTrue、タイムアウトになったらFalse。
	@auther Egtra
	*/
	Static Function afterWait(ret As DWord, n As DWord) As Boolean
		Select Case ret
			Case WAIT_TIMEOUT
				Return False
			Case WAIT_ABANDONED
				Throw New AbandonedMutexException
				Debug
				ExitThread(0)
			Case WAIT_FAILED
				ActiveBasic.Windows.ThrowWithLastError()
			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

	/*!
	@brief WaitHandleのリストをHANDLE配列にする。
	@param[in] handles 変換元
	@return 変換済みの配列の要素を指すポインタ
	@date 2008/08/27
	@auther Egtra
	*/
	Static Function ToArrayOfHANDLE(handles As Collections.Generic.IList<WaitHandle>) As *HANDLE
		Dim count = handles.Count 
		ToArrayOfHANDLE = GC_malloc(count * SizeOf(HANDLE))
		Dim i As Long
		For i = 0 To ELM(count)
			ToArrayOfHANDLE[i] = handles.Item[i].h
		Next
	End Function

	/*!
	@brief Long値が0以上か-1でない場合、例外を投げる。
	@param[in] x テストする値
	@throw ArgumentOutOfRangeException -1未満の値のとき
	@date 2008/08/27
	@auther Egtra
	*/
	Static Sub ThrowIfInvalidLongValue(x As Long)
		If x < -1 Then
			Throw New ArgumentOutOfRangeException("millisecondsTimeout")
		End If
	End Sub
End Class

End Namespace 'Threading
End Namespace 'System
