Hi people,
I just though of providing you the solution to my question, after a long night of experimentations, hoping to poke some further discussions/ideas. So here it is:
Protected selFeatures As Collection(Of Feature)
Private Delegate Function SpatialQuery(ByVal searchArea As BaseShape, ByVal columns As ReturningColumnsType) As Collection(Of Feature)
Private cancelPendingjob As Boolean = False
Private aResetEvent As New ManualResetEvent(False)
Public Function GetTrackInfo(ByVal searchArea As ThinkGeo.MapSuite.Core.BaseShape) As System.Collections.ObjectModel.Collection(Of ThinkGeo.MapSuite.Core.Feature)
cancelPendingjob = False
aResetEvent.Reset()
Try
If Not aTrackLayer Is Nothing Then
aTrackLayer.Open()
RaiseEvent queryProgress(0, "About to select tracks. Running spatial query locally.")
Dim aSpatialQuery As New SpatialQuery(AddressOf aTrackLayer.QueryTools.GetFeaturesIntersecting)
Dim aSpatialQueryAsyncState = aSpatialQuery.BeginInvoke(searchArea, ReturningColumnsType.AllColumns, AddressOf InvokeAsyncStateHandlerSpatialQuery, aSpatialQuery)
Console.WriteLine("About to block")
aResetEvent.WaitOne()
Console.WriteLine("Unblocked")
aTrackLayer.Close()
.....
End Function
Public Sub CancelRunningQuery() Implements IAISAnalisysElement.CancelRunningQuery
aResetEvent.Set()
cancelPendingjob = True
End Sub
Private Sub InvokeAsyncStateHandlerSpatialQuery(ByVal ar As IAsyncResult)
Try
If ar.AsyncState IsNot Nothing AndAlso TypeOf ar.AsyncState Is SpatialQueryThen
Dim invocation_results = CTypeDynamic(Of SpatialQuery)(ar.AsyncState).EndInvoke(ar)
If invocation_results IsNot Nothing Then
selFeatures = invocation_results
aResetEvent.Set()
End If
End If
Catch ex As Exception
Console.WriteLine("Opps")
End Try
End Sub
The way it works is pretty simple. The GetTrackInfo Function needs to run a Spatial Query on a layer. Instead of calling the spatial query directly it creates a new delegate of type SpatialQuery that has of course the same signature with the spatial query we need to run. Then it calls the BeginInvoke of the delegate, passing as parameters the parameters of the actual SpatialQuery, the address of a Sub that will handle the response, InvokeAsyncStateHandlerSpatialQuery in our case, and of course the Delegate instance itself. This has to happen because when the InvokeAsyncStateHandlerSpatialQuery returns we must be able to get back our results as we will see later. In a normal run, the GetTrackInfo will BLOCK at aResetEvent.WaitOne() waiting for the delegate to return at its handler i.e at the InvokeAsyncStateHandlerSpatialQuery. This is the "catch" since now we have delegated the actual Query to another thread and our code is waiting for a signal that we control i.e the aResetEvent. When the InvokeAsyncStateHandlerSpatialQuery returns we simply call the EndIvoke where we obtain our results. In this case the collection of features and we pass it to the variable the is visible to the whole class i.e Protected selFeatures As Collection(Of Feature). Finally we signal our waiting Function to wake up by "setting" the "aResetEvent" In the scenario that we want to simply Cancel the query we call the Sub CancelRunningQuery() which also signal our ResetEvent, thus causing the blocked function to proceed. In this scenario though, the function (missing code) will have to handle the empty features of exit because the cancelPendingjob boolean is also set to false. I wish I had not used the reset event and simply Close the Feature Layer causing the Query to fail with an exception within the InvokeAsyncStateHandlerSpatialQuery. That would give me a cleaner implementation. Unfortunately when I tried to close the layer from the Sub CancelRunningQuery() nothing happened. I guess it must have something to do with the threading model. So the only "gray" area in the code is there where the InvokeAsyncStateHandlerSpatialQuery will return long after a cancellation has happened. Nevertheless I have yet to see "such" a return so I assume that some kind of garbage collector kills it silently. A final point regards the actual SpatialQuery function that I tried to use. When I created the delegate, it appeared that its signature was: GetFeaturesIntersecting(ByVal searchArea As BaseShape, ByVal columns As ReturningColumnsType) As Collection(Of Feature) Wouldn't this be better be ByRef for the BaseShape as coping a whole geometry is time and memory expensive? Anyway I hope that my bubbling was of some use. I hope that the ThinkGeo people will look into my snippet and will suggest better tweaks. I would really like to kill the running Query with some kind of signal or by closing the layer. Kind Regards Yiannis