Jump to content
The simFlight Network Forums

FSUIPC 2.4 multithreading problem


AJ212

Recommended Posts

Hi,

I'm using FSUIPC since more than 3 years now and didn't face a issue when reading Offsets form FSX with FSUIPC. I've recently installed FSUIPC 2.4 to check on my multithreading timing issue I encountered after changing my program code (VB2010 - .net) to accelerate my program.

Here's the story: Before changing my code I was reading Offset from FSX in Serial that means I (more or less) have a List including several "HEXcodes" and corresponding "VARsizes"... Depending on the "VARsize" I am creating the offset  (short, integer, double, aso...). I am polling thru the list, creating the new Offset with a "groupname" afterwards I am setting up the Process call with the group name and after retrieving the value I am deleting the group again... Depending on the length of the list a loop takes >2sec. Now I decided to split the offset list in parts depending on how many HW Output-Cards I am using (which in my case could be up to 24)

...and herewith comes the problem: When splitting up the list I was creating a thread for each single List (for test purposes I am using just 3 for now...)  Now each threat creates its own offset (again depending on VARsize") and sets up the process call (by group name) and deletes the group again... Each thread has its unique group name so FSUIPC doesn't get "confused"...

When starting may program this "multi-threading process call" works for about 10sec. afterwards I am getting weird errors like "index out off array area" during creating an offset or "Offset Group does not exist" during a process call aso... The funny thing is, that if I mark this line then "Set next statement" it works again for a few sec. until the next error pops up form another thread (out of 3)

Unfortunately I was running out of ideas to overcome this problem so I really would appreciate if somebody could help on this topic... I also was think about to open multiple instances (FSUIPC.open) ...one for each thread but I don't know if that is even possible...

Thanks for any information!!!

Regards,

AJ212 

Link to comment
Share on other sites

Hi,

While the Process() call is thread safe, the creation of offsets is not.

It's not possible to open multiple connections for each thread. I'm not sure that would have any advantage because requests from each connection would be put on the same windows message queue when being sent to FSUIPC. So they wouldn't be executed in parallel.

I think the best solution will be to make creating offsets thread-safe inside the DLL. The best way of doing that would be to use the new 'concurrent' collections from .NET 4.0 inside the DLL. This would maintain most of the performance from using multiple threads. The alternative is basic locking of the offset collections but then each thread will need to queue for the lock.

If you're happy to only target .NET 4.0 or later, and to upgrade to version 3.0 of the DLL then I'll make the changes to 3.0 towards the end of this week. Let me know if this sounds okay for you.

Paul

 

Link to comment
Share on other sites

Hi Paul,

 

Thanks for your fast reply! I relay appreciate your offer and I'm absolutely willing to go to 3.0 with .net 4 or higher...

making the DLL thread-safe when creating offsets sounds good! Now, for my understanding, that actually means each single thread will queue at the point of code, where I am creating an offset until the last thread has finished creating it, right?

Regards,

AJ212

Link to comment
Share on other sites

Quote

>> that actually means each single thread will queue at the point of code, where I am creating an offset until the last thread has finished creating it, right?


The queuing will mainly occur when calling Process() as there is only one connection and only one message queue in Windows on which to send messages to FSUIPC. Each thread trying to Process() will be queued and given access to the Process() method one at a time.

By using the new 'concurrent' collections, the blocking when creating and deleting offsets will be minimised. It will only happen if the threads are accessing the same 'bucket' in the Dictionary. This is pretty random though as it depends on the key used and the hashing algorithm used by .net. I think most of the time it won't block.

 

Reading your original post it seems like you're calling Process() for each offset. That is, you only have one offset in the group. I may have misunderstood, but this is very inefficient as the Process() call is the one that takes a bit of time (it's exchanging data with FSUIPC). So if you have 10 offsets, calling process 10 times (once for each offset) will be 10 times slower than putting all 10 offsets into one group and calling process() once.

Of course you'll still have the threading problems, but I just wanted to point that out.

Paul

Link to comment
Share on other sites

Attached is the new DLL with the changes,

Adding and removing offsets and groups is now thread-safe. I've run tests with 100 threads all creating a new offset, processing it and deleting the group. Seems to work okay here.

However, I did run into another problem which you might also be having:

I was running the threads from System.Timers.Timer classes. If I ran the timers too fast then I would get an error that the group doesn't exist. This is the cause:

1. Thread A is created and the timer code is run.

2. Thread A creates the offset in a group of called "Group1".

3. Thread A Processes the group

4. Before this has finished the timer elapses and creates Thread B and runs the timer code

5. Thread B Adds offsets to the group called "Group1"

6. Thread A finished the Process() call and deletes "Group1"

7. Thread B tries to process "Group1" but it's deleted.

 

This is something that you'll need to manage in your code, either by:

A. creating your own locking mechanism so the timer doesn't run the code if the last timer tick hasn't finished.

b. Increasing the time between timer ticks so no overlap occurs.

However you could also try an experimental feature I've added to the DLL. There is a new property called:

FSUIPCConnection.GroupsIsolatedToThread

This is normally false. If you set this to true right after you open the connection, then all groups/offsets are unique to the thread they are created on. Therefore you can have a group with the same name created on different threads. A thread can not process or delete a group created on another thread.

Setting this to true will also solve the "timer too fast" problem described above. If the timer ticks again before the code from the last tick is completed, the timer code will be run on a new thread. This will then have its own copy of the offset group and won't be affected by the previous thread when it deletes the group.

Let me know how it goes.

Paul

FSUIPCClient3.0_RC2.zip

Link to comment
Share on other sites

Hi Paul,

I've just tested 3.0 and encountered the same problem... :( I hope I did it right by just replacing the old DLL with the new one... I also have implemented a "sleep" for each thread once it has gone thru the list before starting the next loop... I've tested 10, 20, 50, 100ms and it just delays the alarm "offset doesn't exit" or "Index out of Array Area"...

In regards of your question... YES, I am calling Process () every single time after creating an offset... the reason why I'm doing that is that each offset gets created with the same NAME (Dim FS_Value...) depending on VARsize (FS_Size) again it looks like this:

'it start with... for i = 0 to end of list.... aso...

Select Case FS_Size

Case 1

Dim FS_Value As Offset(Of Byte) = New Offset(Of Byte)(ThreadGroup, FS_Offset)

FSUIPCConnection.Process(ThreadGroup)

'write FS_Value.Value to somewhere else to work with it...

Case 2

Dim FS_Value As Offset(Of Short) = New Offset(Of Short)(ThreadGroup, FS_Offset)

FSUIPCConnection.Process(ThreadGroup)

'write FS_Value.Value to somewhere else to work with it...

Case 4

Dim FS_Value As Offset(Of Integer) = New Offset(Of Integer)(ThreadGroup, FS_Offset)

FSUIPCConnection.Process(ThreadGroup)

'write FS_Value.Value to somewhere else to work with it...

Case 8

Dim FS_Value As Offset(Of Double) = New Offset(Of Double)(ThreadGroup, FS_Offset)

FSUIPCConnection.Process(ThreadGroup)

'write FS_Value.Value to somewhere else to work with it...

End Select

FSUIPCConnection.DeleteGroup(ThreadGroup)

This selection above is looping thru a for loop from the first element in the list to the last one...

Hence, I am calling process () for every single after creating an offset while polling thru all item of the list...

I also was thinking about creating some sort of "OffsetArray" in the for loop and each one will be member of the same "ThreadGroup" and afterwards I wanted to call Process(ThreaGroup) instead of calling Process() for every single Offset I've created in my existing code... But I don't know how to create such a "OffsetArray" with FS_Value(i) where (i) stands for the "for loop counter"

I hopy you know what I mean now... For every (i) in the loop I want to create a Offset before Calling Process("ThreadGroup")

...something like this:

Dim FS_Value(i) As Offset(Of Double (Depends on the List(i) VARSize)) = New Offset(Of Double (Depends on the List(i) VARSize))(ThreadGroup, FS_Offset(i) (equals to List(i) HEXCode))

THX! AJ212

Link to comment
Share on other sites

With 2.4 you couldn't easily create a collection of offsets. With 3.0 there is a new untyped offset class that you can use to make this possible. It's also called Offset but you just don't give it a type.

Instead of this

Dim myOffset as Offset(Of Integer)(&H2BC)

it's just this

Dim myOffset as Offset(&H2BC, 4)

Note that because there is no type the size is always required.

Also because you don't set the type when you create it, you need to specify the type when you get the value:

Dim myValue as Integer = myOffset.Value(Of Integer)

The main advantage of these untyped Offsets is that you can put them in a collection or array e.g.
 

Dim myOffsets as List(Of Offset)
Dim myOffset as Offset()

Below is a small piece of example code that has a list of offset addresses/sizes and then creates another list of proper Offsets from those. It then processes the group and gets the value back from all the offsets.

The class that holds the offset address and size is just this: (You'll already have your own method for storing your list of addresses. This is just for the example).

Public Class offsetInfo
    Public address As Integer
    Public size As Byte
End Class

This is the example code:
 

    Public Sub OffsetListDemo()
        ' dummy list that holds a list of offset address, sizes etc to be retreived
        Dim offsetInfos As List(Of offsetInfo) = New List(Of offsetInfo)
        ' create 3 dummy offsets
        offsetInfos.Add(New offsetInfo() With {.address = &H36C, .size = 1}) ' stall warning
        offsetInfos.Add(New offsetInfo() With {.address = &H366, .size = 2}) ' On ground
        offsetInfos.Add(New offsetInfo() With {.address = &H2BC, .size = 4}) ' IAS

        ' Create a list to hold the offsets
        Dim offsetList As List(Of Offset) = New List(Of Offset)()

        ' Create an Offset for each address in the list
        For Each info As offsetInfo In offsetInfos
            Dim nextOffset As Offset = New Offset("myGroup", info.address, info.size)
            offsetList.Add(nextOffset)
        Next info

        ' process all the offsets in this group
        FSUIPCConnection.Process("myGroup")

        ' go through the offsets and get the values
        For Each fsOffset As Offset In offsetList
            Select Case fsOffset.DataLength
                Case 1
                    Dim value As Boolean = fsOffset.GetValue(Of Boolean)
                Case 2
                    Dim value As Boolean = fsOffset.GetValue(Of Short)
                Case 4
                    Dim value As Boolean = fsOffset.GetValue(Of Integer)
            End Select
        Next fsOffset
    End Sub

 

I'm not sure about your threading problem. You might need to post more code, or a simple project that demonstrates the error.

Paul

 

Link to comment
Share on other sites

Paul,

...you're a genius :) 3.0 is working!!!

From what I've seen today it seems to work pretty well :)

The problem was on my side... Remember I was asking you if I just have to copy/paste FSUIPC 3.0 into my Ref folder of my VB prog... That's what I did without checking if my App took over the correct version...

Today morning I checked that and I saw that I was still running in 2.0.4xxx ooops... The reason for that was a old reference to a wrong folder... my fault...

After correcting the folder ref and adding a new ref for FSUIPC 3.0 the version has been changed to 3.0.xxxx

Now all threads (currently only 3 but soon it will become more :) ) are running!!!

So, creating one offsets calling process() and deleting the offset works! The new timing went down to approx. < = 100ms per thread... I still have to test that stuff with more offsets per thread-list to find out how the timing behaves...

 it still might be, that I might go for the suggestion you made with the offset group and call process() not only for a single offset but for the whole list at once... because of calling process() "taking long"...

I hope I can get back to you in case I get a problem with creating this sort of offset group...

Many thanks for you help on that!!!!!!!

Regards,

AJ212

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use. Guidelines Privacy Policy We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.