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.

Where Next?

Popular Frontend topics Top

palak231
Hi this is Palak Sharma I am new here and I found this community while researching about JavaScript over the internet. Well I have comp...
/js
New
pavanforza
I have a requirement to extract data from firebase which is used to build serverless applications. Can we connect Firebase no-sql databa...
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
harwind
First have a look at the code: function mainfunc(func, par3, par2){ window[func](par3, par2); } function calledfunc(par3, par2){ ...
/js
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
WiseDan
hi everybody , am new in gsap.js so i wanted load content in my home page when user scrolling , but since am reading the documentation ...
New
JessicaW33
Hello All, How would you implement a navigation system in React Native? Could not find a way so I ask the community can someone help me ...
New
JessicaW33
Hello everyone, I’m working on optimizing the performance of React Native applications and would like to gather insights and suggestions...
New
sona11
I’m currently working on a JavaScript project that involves converting user-supplied text to numbers. Dealing with different areas and th...
/js
New
pjamesrud
I am creating an app that allows user to enter values and depending on the value a number of textviews are changed programmatically from ...
New

Other popular topics Top

AstonJ
What chair do you have while working… and why? Is there a ‘best’ type of chair or working position for developers?
New
malloryerik
Any thoughts on Svelte? Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue...
New
dasdom
No chair. I have a standing desk. This post was split into a dedicated thread from our thread about chairs :slight_smile:
New
PragmaticBookshelf
Rust is an exciting new programming language combining the power of C with memory safety, fearless concurrency, and productivity boosters...
New
PragmaticBookshelf
“Finding the Boundaries” Hero’s Journey with Noel Rappin @noelrappin Even when you’re ultimately right about what the future ho...
New
PragmaticBookshelf
Learn different ways of writing concurrent code in Elixir and increase your application's performance, without sacrificing scalability or...
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
If you get Can't find emacs in your PATH when trying to install Doom Emacs on your Mac you… just… need to install Emacs first! :lol: bre...
New
PragmaticBookshelf
Author Spotlight Jamis Buck @jamis This month, we have the pleasure of spotlighting author Jamis Buck, who has written Mazes for Prog...
New
sir.laksmana_wenk
I’m able to do the “artistic” part of game-development; character designing/modeling, music, environment modeling, etc. However, I don’t...
New