airsdk / Adobe-Runtime-Support

Report, track and discuss issues in Adobe AIR. Monitored by Adobe - and HARMAN - and maintained by the AIR community.
200 stars 11 forks source link

[AIR 51.1][iOS] `FileStream::openAsync()` inside `Worker` cause hang #3394

Open itlancer opened 1 month ago

itlancer commented 1 month ago

Problem Description

FileStream::openAsync() call inside Worker cause hang for iOS patform using AIR 51.1.

Tested with AIR 51.1.1.1 and AIR 51.1.1.2 with multiple different iOS devices with different OS versions and with different applications. Same problem in all cases. There is no such problem using synchronous method to work with FileStream. There is no such problem with AIR 51.0.1.5 and below. Also there is no such issue with other platforms. Didn't tested with tvOS.

Related issue: https://github.com/airsdk/Adobe-Runtime-Support/issues/2442

Steps to Reproduce

Launch application with code below with any iOS device. Application open file asynchronously in Worker.

Application example with sources and Scout logs attached. air51_ios_worker_bug.zip

package {
    import flash.display.Sprite;
    import flash.system.Worker;
    import flash.system.WorkerDomain;
    import flash.events.Event;
    import flash.filesystem.File;
    import flash.filesystem.FileStream;
    import flash.filesystem.FileMode;

    public class AIR51IOSWorkerBug extends Sprite {
        private var worker:Worker;
        private var sprite:Sprite = new Sprite();

        public function AIR51IOSWorkerBug() {
            if (Worker.current.isPrimordial) {
                trace("main");
                addEventListener(Event.ADDED_TO_STAGE, init);
            } else {
                trace("worker");

                var fileStream:FileStream = new FileStream();
                fileStream.openAsync(File.applicationStorageDirectory.resolvePath("test.txt"), FileMode.APPEND);//This line cause application hang
                //fileStream.open(File.applicationStorageDirectory.resolvePath("test.txt"), FileMode.APPEND);//Use sync mode as a workaround
                fileStream.close();
            }
        }

        private function init(e:Event):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);

            sprite.graphics.beginFill(0xff0000);
            sprite.graphics.drawRect(0, 0, 100, 100);
            sprite.graphics.endFill();
            sprite.x = 100;
            sprite.y = 100;
            addChild(sprite);

            addEventListener(Event.ENTER_FRAME, enterFrame);

            worker = WorkerDomain.current.createWorker(this.loaderInfo.bytes, true);
            worker.start();
        }

        private function enterFrame(e:Event):void {
            sprite.rotation += 1;
        }
    }
}

Actual Result: Application hang. No exceptions, no errors. Visually application could works few frames, than hang and nothing happens. Main thread in Scout also hangs at this moment. But Worker thread continue to work.

Expected Result: Application works without hangs.

Known Workarounds

1) Use synchronous methods to work with files via FileStream using Worker. 2) Use AIR AIR 51.0.1.5 or below.

itlancer commented 1 month ago

@ajwfrost With synchronous FileStream work the same hang. FileStream::writeUTFBytes() cause hang. Sample:

package {
    import flash.display.Sprite;
    import flash.system.Worker;
    import flash.system.WorkerDomain;
    import flash.events.Event;
    import flash.filesystem.File;
    import flash.filesystem.FileStream;
    import flash.filesystem.FileMode;

    public class AIR51IOSWorkerBug extends Sprite {
        private var worker:Worker;
        private var sprite:Sprite = new Sprite();

        public function AIR51IOSWorkerBug() {
            if (Worker.current.isPrimordial) {
                trace("main");
                addEventListener(Event.ADDED_TO_STAGE, init);
            } else {
                trace("worker");

                var fileStream:FileStream = new FileStream();
                fileStream.open(File.applicationStorageDirectory.resolvePath("test.txt"), FileMode.APPEND);
                fileStream.writeUTFBytes("test");//This line cause application hang
                fileStream.close();
            }
        }

        private function init(e:Event):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);

            sprite.graphics.beginFill(0xff0000);
            sprite.graphics.drawRect(0, 0, 100, 100);
            sprite.graphics.endFill();
            sprite.x = 100;
            sprite.y = 100;
            addChild(sprite);

            addEventListener(Event.ENTER_FRAME, enterFrame);

            worker = WorkerDomain.current.createWorker(this.loaderInfo.bytes, true);
            worker.start();
        }

        private function enterFrame(e:Event):void {
            sprite.rotation += 1;
        }
    }
}

So FileStream in Worker with iOS completely broken with AIR 51.1 right now.

itlancer commented 3 weeks ago

@ajwfrost Issue still exists with latest AIR 51.1.1.3.

Hackquarius commented 3 weeks ago

I can confirm my game hangs on AIR 51.1.+ for no reason on startup. Newest version works good on Desktop and Android

Using AIRSDK 50.2 seemed to worked to package a release for iOS. Apple accepted the .IPA the 1st version of my game is set to release soon. :)

ajwfrost commented 3 weeks ago

@Hackquarius do you think it's this same issue? The other potential start-up issue could be around use of EncryptedLocalStore. We'll be trying to get a fix out for both this and the outstanding ELS things soon...

Hackquarius commented 3 weeks ago

Hi,

@ajwfrost I'm not sure if it's the same issue, but I tried removing Launchscreen.storyboardc on iOS based on another thread's suggestion. However, it didn't seem to fix the hang-up.

After reviewing my code, I noticed this line at the end of the startup loading process for the main menu of my game:

saveDataObject = SharedObject.getLocal("LoadAllStats");

I usually load all the SharedObject data from memory, and it works fine. Do I need to switch to EncryptedLocalStore on iOS now with the new AIR 51 SDK?

Hackquarius commented 3 weeks ago

@ajwfrost quick update, I have commented this code out

// saveDataObject = SharedObject.getLocal("LoadAllStats");

Now, with iOS on AIR SDK 51, the app no longer hangs, but I still need to load the saved data for my players. :)

ajwfrost commented 3 weeks ago

Okay that's interesting / odd !! We'll check that too, might need a new bug for it if SharedObject.getLocal is causing it to hang too. Might be the same root cause though.. thank you for the details!

ajwfrost commented 2 weeks ago

So the issue here - at least for the use of the Worker thread also kicking off an async process - was due to some changes we made recently to handle asynchronous callbacks in the main/UI thread, needed for handling network connection callbacks. But actually, we should have been handling these in the "player" thread i.e. whatever thread that player instance was created in. So for the main/primary runtime instance, that is the same as the UI thread, but for a worker, it's different.

Now though, I'm not sure whether the same thing would cause the problem with SharedObject.getLocal(), unless that was being called from a Worker (@Hackquarius - are you using Workers, it wasn't clear from the above?)

thanks

Hackquarius commented 2 weeks ago

Hi @ajwfrost ,

Just a simple code snippet with no workers involved. Maybe it's related to the Starling Asset Manager?

Before initializing the Game class from Starling, I create a public var. public var saveDataObject:SharedObject;

then the saveDataObject is called using SharedObject.getLocal("LoadAllStats36") after loading asset.

There are various data objects handledright after, like checking

        if (saveDataObject.data.HeroLoad_XPTotal == null)
        { 

        }
        else
        { 

            XP_Total = saveDataObject.data.HeroLoad_XPTotal;    

        }
ajwfrost commented 2 weeks ago

@Hackquarius we're not able to reproduce this freeze .. can't see anything in the SharedObject handling that might trigger asynchronous updates, plus with only one thread involved anyway, it shouldn't actually hit anything that would lock it.

So I can't actually see why your case might hang, and I can't see how our fix for the original issue here would impact anything other than Worker instances.

If you're able to create a simple test case that exhibits the problem as a standalone SWF, and send that to us (or attach it here), we can try to reproduce it. But just adding the above code snippets to our constructor is not causing any problems, it seems to work as expected.

thanks

Hackquarius commented 2 weeks ago

@ajwfrost I'll have time to check it out soon. I'm currently completing the first release of my game Cosmic Guardians and have been pretty busy fixing some small issues with both the game and the website! It's already available on Android, and will be coming to Steam on September 2nd. I'm also finishing the Apple release as I write this post.

I'm very excited as this is my first official game on Steam! For development, I used the latest AIR 51.1 for Steam and Android, and had to use 50.2 for iOS. I'll look into why the hangup is occurring with 51.1 on iOS so that the game can eventually use the latest AIR SDK across all platforms.

ajwfrost commented 2 weeks ago

Looks good! Might have a play on that next week :-)

RossD20Studios commented 1 week ago

Hi @Hackquarius - I believe I, too, have encountered your bug as today was the first time I tried to publish my game, Abalon, to iOS with the latest AIR 51.1. (Previously, worked great in 50.2).

To help shed some light on possible causes, here's what I observed:

  1. My game immediately locks up during the "Loading Assets" phase when trying to run the game on anything but a debug connection. If I try to load in debugger to identify the error, the game loads correctly.
  2. Based on your notes above, I suspect we have some similarities in our game structure. Specifically, during my loading phase, I am loading a SQLLite database containing our game definitions. This database is loaded by both the Main thread and the backworker thread. Our AI engine runs on the backworker, so it needs to load the definitions stored in the database just like the player in order to understand how to play the game. It appears that this is the point at which the game is failing to load.
  3. I am not using an asynchronous connection to load the database, but rather a sync connection as you can see in my code snippet below: Screenshot 2024-09-04 at 12 16 28 PM

Hope this helps! I'll continue investigating and provide additional notes here on my findings.

Hackquarius commented 4 days ago

Hi @ajwfrost and @RossD20Studios ,

I tried using the new AIR 51.1.1.4 to see if it would fix the hang issue on my iOS device (iPad Air 4th Gen). Unfortunately, it still hangs. After releasing my game on Steam, Android, and iOS https://cosmicguardians.com/, I finally have some time to investigate further.

I discovered that everything works fine when loading assets, except when it involves assets loaded within a loop. For example:

for (y77 = 1; y77 <= Number_Asteroids1_MAX; y77++) {}

In my case, the variable Number_Asteroids1_MAX has 500 asteroids. This works perfectly on AIR 51.4 for PC and Android, but iOS seems to hang whenever the loop is executed. I added a trace in the code, and it shows that the loop continues infinitely if Number_Asteroids1_MAX is 2 or more. However, when I set Number_Asteroids1_MAX = 1, the loop skips to the next one, but then it hangs again.

The error on iOS seems to occur when loading assets in loops, particularly when using the Starling asset manager. Interestingly, the next loop that causes the hang doesn’t even involve images; it happens while generating multiple coordinates using numbers.

This is what I've observed while testing the new AIR SDK.

Hackquarius commented 4 days ago

Just a quick note about SharedObject.getLocal("LoadAllStats36"). When it was commented out, the debug threw an error, but I was still able to continue, and the loops worked fine. I had been building a standard build for iOS earlier and didn't notice the error because I hadn't tried debugging first.

RossD20Studios commented 4 days ago

Thanks for the update @Hackquarius and congrats on your game launch!

@ajwfrost I tested the new AIR SDK today as well and pleased to say it did fix the loading issue I was experiencing. I also played some battles vs. the AI (which uses the backworker) and these seemed to work fine, too. Our AI uses many loops, so I'm not sure how @Hackquarius issue relates, perhaps it is specific to for loops within the loading sequence?