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

pillaiindu
Some days ago I came across a video teaching the internals of git. It had some nice diagrams and animations. The diagrams looked like han...
New
Sally_san
So I’ve got a client that wants to build a site like this: Specifically 3 things from the website: The fullscreen sections that chang...
New
jubocade
What is the best course of front end (live webinars or recorded)? So I already have basic understanding of HTML CSS JS and React but I wa...
New
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
sona11
What is the difference between tuple relational calculus (TRC) and domain relational calculus (DRC)? What distinguishes them from relatio...
New
Fl4m3Ph03n1x
Background I have a Phoenix application, where all pages (expect the login page) have a menu at the top. This menu will therefore only 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
Fl4m3Ph03n1x
Background I have a custom component in my LiveView, which is basically a group of checkboxes. I want to add a new attribute to my custo...
New
Fl4m3Ph03n1x
Background I have Phoenix umbrella application. When inside said application, I can run it without issues if MIX_ENV=prod. However, if I ...
New
grboi
I am learning full-stack and wanted to know which tech would be best, which would scale better. This is because when sitting for intervie...
New

Other popular topics Top

PragmaticBookshelf
Brace yourself for a fun challenge: build a photorealistic 3D renderer from scratch! In just a couple of weeks, build a ray tracer that r...
New
Exadra37
Please tell us what is your preferred monitor setup for programming(not gaming) and why you have chosen it. Does your monitor have eye p...
New
New
PragmaticBookshelf
Build highly interactive applications without ever leaving Elixir, the way the experts do. Let LiveView take care of performance, scalabi...
New
Margaret
Hello everyone! This thread is to tell you about what authors from The Pragmatic Bookshelf are writing on Medium.
1147 29994 760
New
DevotionGeo
I have always used antique keyboards like Cherry MX 1800 or Cherry MX 8100 and almost always have modified the switches in some way, like...
New
New
New
RobertRichards
Hair Salon Games for Girls Fun Girls Hair Saloon game is mainly developed for kids. This game allows users to select virtual avatars to ...
New
PragmaticBookshelf
Write elegant Ruby code—not just correct, but idiomatic. Go beyond the vocabulary and syntax and learn to express yourself with eloquence...
New