gameengineer

gameengineer

Correct thread-safe way to share a resource using Android Studio, kotlin and java?

We are developing on Samsung Tab Active 4 Pro using Android Studio, kotlin and java. We are getting what we think are app deadlocks. The app runs for a few minutes then freezes. I am not new to developing with threads and synchronization methods however I am very new to Android development. I assume I am doing something wrong and will share what I am doing. But to set up the situation, we are using OSMDroid for the map viewing and are using layers of the map for draw icons that represent aircraft flying over the map. The main android app is done in kotlin and the UDP over ethernet is written in java files. Aircraft telemetry is received at about 1 hz over ethernet inside protobuf packets. The protobuf is received and parsed in a Listener java class using a thread and implemented in the run() method. In the udp handler where we receive and parse protobuf we call a kotlin object “class” (unfortunate use of terms with kotlin) that has a method UpdateEntity(). Here is a snippet of this code that handles a proto message type called Entity. EntityClass is the java wrapper created. This part is working fine, no issues here, just showing for context.

(java)

 if (msg.getMessageTypeEnum() == MessageWrapperClass.MessageWrapper.MessageTypeEnum.MESSAGE_TYPE_ENTITY) {
                    EntityClass.Entity entity = msg.getMessageEntity();

               . . . (code deleted for brevity)

                    Datasource.INSTANCE.UpdateEntity(unit);  // calls Kotlin object

                } 

Datasource.INSTANCE.UpdateEntity(unit); call accesses the share list. Here is that method in total. Note Datasource is that singleton-like object construct that has all public “static” methods. (I’m a C++ guy mostly :slight_smile: New to kotlin)


/**
 * [Datasource] holds the data that is utilized by the UI
 */
object Datasource {


    val entityUnitListMutex  = Mutex(false)
 
    /*
    This is the list of units. Any unit added to this list will get added to the map.
     */
    val entityUnitList =  mutableStateListOf<EntityUnit>()

    fun UpdateEntity(entity:EntityUnit)
    {
        entity.lifeTime_ms = System.currentTimeMillis() // update its life time

        try {
            // NOTE - FindEntity() call must remain OUTSIDE of runblocking and mutex because
            // FindEntity also has mutex and we do not want to nest mutex calls. Deadlock may occur
            val existingEntity = FindEntity(entity.id)

            runBlocking {
                entityUnitListMutex.withLock {
                    if (existingEntity != null) {
                        entity.status = existingEntity.status
                        entity.missionCount = existingEntity.missionCount
                        val index = entityUnitList.indexOf(existingEntity)

                        entityUnitList[index] = entity

                    } else {
                        entityUnitList.add(entity)
                    }
                }// end mutex
            }
        }
        catch(e:Exception)
        {
            Log.e(ContentValues.TAG, "Datasource::UpdateEntity " + e.toString())
        }
    }


    /*
    Find the entity in the list of active entities
     */
    fun FindEntity(entityName: String): EntityUnit? {
        try {
            var returnval: EntityUnit? = null
            runBlocking {
                entityUnitListMutex.withLock {
                    for (i in entityUnitList.indices) {
                        if (entityUnitList[i].id == entityName) {
                            returnval = entityUnitList[i]
                            break
                        }
                    }
                }// end mutex
            }

            return returnval
        } catch (e: Exception) {
            Log.e(ContentValues.TAG, "Datasource::FindEntity " + e.toString())

            return null
        }
    }

My understanding of kotlin and android is that you need to use a coroutine if you want to use a mutex. I used a mutex to protect the shared resource, the entityUnitList of type mutableStateListOf

The Listener is started up on the main, UI thread however it uses its own thread to receive udp packets. The call to Datasource::UpdateEntity() is done in that thread, not the UI thread. Datasource is used by the UI thread as well and it needs access to the entityUnitLst to read from. The UI thread does not write to any of the elements in the list nor does it add or subtract elements from that list.

So the problem is the app will lock (not crash and disappear) and be non-responsive or frozen. This happens at various times but typically within a minute or two. My best guess is a thead race condition.

There was someone else developing the UI portions of this app and they said the UI only updates when a data element of it changes. So in UpdateEntity() these two lines do cause a UI refresh.

entityUnitList[index] = entity
entityUnitList.add(entity)

Here is another place accessing that list and is in the UI thread


fun PutStuffOnMap(
    uiState: FOXUiState, mapView: MapView, onEntityClicked: (entityClicked: String) -> Unit
) {
    try {
        val items = ArrayList<OverlayItem>()

        runBlocking {
            Datasource.entityUnitListMutex.withLock {
                for (i in entityUnitList.indices) {
                    val entity = entityUnitList[i]
                    PlotEntity(mapView, entity)?.let { items.add(it) }
                }
            }// end mutex
        }
. . . .

There is more to that function but that function is called from here in a global method tha tis used in the map drawing code.

I am thinking we have a mutex locking unlocking issue where it stays locked and the UI blocks with that runBlocking{ } block waiting for the unlock. The Listener gets in and gets out when updating the entity pretty quick. The only link to the UI thread is Datasource::UpdateEntity(). When we comment out UpdateEntity() we never deaklock or freeze the app. We can even pre-load entityUnitList with entity class elements and interact with them again with no app freeze. Its only when we enable getting movement updates over ethernet (using USB/Ethernet adapter plugged into the tablet) when we freeze the app eventually.

So I think using runBlocking is wrong so what is the correct way to share that entityUnitList over multiple threads safely? We do not have to use a mutex and we could consider other list types that are better with thread synchronization.

Popular Frontend topics Top

davearonson
I’m unit-testing some JS, with Jasmine, and I’d like to check our coverage. We’re not using any front-end framework, nor much JS, so no ...
New
The_Exile
I am new to programming. I started reading Eloquent Javascript 3rd Edition, as the book comes highly recommended as a good place for beg...
/js
New
brian
I’m working with a designer who created a design in Photoshop. I am mostly a Node.js and Android dev, but when I have worked with designe...
New
AlessandroDsgroup
Hi to everyone, we are experiencing a 401 error related to the connection of the websocket (in reference to our web app); we are unable ...
New
tomcatttttt
I’m trying to take the API from the site to get a seamless online game through the HTML5 API. It works in all browsers except Google Chro...
New
Fl4m3Ph03n1x
Background I have a a fresh umbrella project with a Phoenix app inside. To create the app I used the following commands: mix new test_a...
New
harwind
I’m currently working on a front-end development project and I’m facing an issue with aligning items using CSS Flexbox. I want to horizon...
New
Julien0577
Hi all, Anybody knows how to do this menu animation? (from BBVA APP, they have the same for both android and iOS app). Is it custom?...
New
avipal
I have an application where it is three layer 1st layer- A legacy core routines 2nd layer- built on top of the Core routine using Dotno...
New
hosseinkhosromanesh
hello , i should code a cluster like image bellow we have no challenge in coding backend but in front need some clue to do this its a dy...
/js
New

Other popular topics Top

DevotionGeo
I know that these benchmarks might not be the exact picture of real-world scenario, but still I expect a Rust web framework performing a ...
New
AstonJ
You might be thinking we should just ask who’s not using VSCode :joy: however there are some new additions in the space that might give V...
New
AstonJ
In case anyone else is wondering why Ruby 3 doesn’t show when you do asdf list-all ruby :man_facepalming: do this first: asdf plugin-upd...
New
PragmaticBookshelf
Build highly interactive applications without ever leaving Elixir, the way the experts do. Let LiveView take care of performance, scalabi...
New
AstonJ
Seems like a lot of people caught it - just wondered whether any of you did? As far as I know I didn’t, but it wouldn’t surprise me if I...
New
rustkas
Intensively researching Erlang books and additional resources on it, I have found that the topic of using Regular Expressions is either c...
New
AstonJ
Saw this on TikTok of all places! :lol: Anyone heard of them before? Lite:
New
PragmaticBookshelf
Author Spotlight Erin Dees @undees Welcome to our new author spotlight! We had the pleasure of chatting with Erin Dees, co-author of ...
New
husaindevelop
Inside our android webview app, we are trying to paste the copied content from another app eg (notes) using navigator.clipboard.readtext ...
New
AstonJ
If you’re getting errors like this: psql: error: connection to server on socket “/tmp/.s.PGSQL.5432” failed: No such file or directory ...
New