akkadotnet / akka.net

Canonical actor model implementation for .NET with local + distributed actors in C# and F#.
http://getakka.net
Other
4.72k stars 1.04k forks source link

ActorSystem.Create() blocking indefinitely. [Current status of Akka.NET .NET Standard Support] #4054

Closed MaximG1234 closed 4 years ago

MaximG1234 commented 4 years ago

I am using Akka.Net Version 1.3.16 on .NET Standard 2.0 and have tested with multiple other versions of Akka.NET.

Steps to reproduce problem.

  1. Create new Xamarin forms project using Visual Studio 2019
  2. Add the following code on application start.
protected override void OnStart()
{
    using (var system = ActorSystem.Create("client"))
    {
    }
}
  1. Run application.
  2. Note that the line using (var system = ActorSystem.Create("client")) is entered into and blocks indefinitely.

I have checked to ensure:

  1. Internet permission is enabled.
  2. Akka and Akka.Remote is referenced from both the core and android project.

    What else could be wrong here?

Aaronontheweb commented 4 years ago

@MaximG1234 what does your HOCON look like? Because otherwise, there's nothing that should really block there at all, most of the components all start asynchronously.

MaximG1234 commented 4 years ago

Haha, thats about the quickest response I've seen on Github. Thanks Aaron. Give me a minute to work out how do get the hocon output in VS.

MaximG1234 commented 4 years ago

My apologies. I misunderstood your question. I thought you were talking about the phone debug output.

My akka config file looks like this.

akka {  
    log-config-on-start = on
    stdout-loglevel = DEBUG
    loglevel = DEBUG
    actor {
        provider = ""remote""

        debug {  
            receive = on 
            autoreceive = on
            lifecycle = on
            event-stream = on
            unhandled = on
        }

    }
    remote {
        dot-netty.tcp {
            transport-protocol = tcp
            hostname = localhost
        }
    }
}

I have tried using hostname 0.0.0.0 and am injecting the config directly using the constructor overload.

I have also moved the code from the onStart location in the Android app to event handlers and the results are the same.

I have also tried using both the Android emulator and a real device.

However I note that all of these results are from 1 PC running Visual Studio Enterprise 2019 so it is possible its somehow isolated to my machine. I dont have another machine to test on.

Aaronontheweb commented 4 years ago

@MaximG1234

Ok so this is going to sound dumb, but give it a try:

provider = ""remote""

Change this to

provider = remote
MaximG1234 commented 4 years ago

My code was:


var config = ConfigurationFactory.ParseString(@"
    akka {  
        log-config-on-start = on
        stdout-loglevel = DEBUG
        loglevel = DEBUG
        actor {
            provider = ""remote""

            debug {  
                receive = on 
                autoreceive = on
                lifecycle = on
                event-stream = on
                unhandled = on
            }

        }
        remote {
            dot-netty.tcp {
                transport-protocol = tcp
                hostname = localhost
            }
        }
    }
");

CHANGED TO:


var config = ConfigurationFactory.ParseString(@"
    akka {  
        log-config-on-start = on
        stdout-loglevel = DEBUG
        loglevel = DEBUG
        actor {
            provider = remote

            debug {  
                receive = on 
                autoreceive = on
                lifecycle = on
                event-stream = on
                unhandled = on
            }

        }
        remote {
            dot-netty.tcp {
                transport-protocol = tcp
                hostname = localhost
            }
        }
    }
");

Same result...

Aaronontheweb commented 4 years ago

Hmmm... And with the debugger attached, no exceptions get thrown? Wondering if Akka.Remote binding to an inbound socket is the problem here - not sure if that's permitted on Xamarin apps.

MaximG1234 commented 4 years ago

https://www.dropbox.com/s/qc7z6jt9pjq61i6/2019-11-21%2004-29-33.mkv?dl=0

See for yourself ^

Gonna try one last thing for today which is creating a native (i.e. without xamarin.forms) project and seeing what happens.

MaximG1234 commented 4 years ago

Can confirm, same behaviour using Xamarin native.

Aaronontheweb commented 4 years ago

If you turn off Akka.Remote altogether, does it start up?

MaximG1234 commented 4 years ago

Removed all references to Akka.Remote and used akka {} as config file and same result. Emulator doing the same thing too.

Android 9.0 API 28

Aaronontheweb commented 4 years ago

In that case, it means it's an issue with either the HOCON parser or a non-ActorRefProvider component (i.e. EventStream) not loading up properly. My money is on the HOCON parser, which we're due to replace with the stand-alone HOCON library in this Github organization as part of the v1.4.0 final release.

MaximG1234 commented 4 years ago

If I go back to (without the references)


akka {  
    log-config-on-start = on
    stdout-loglevel = DEBUG
    loglevel = DEBUG
    actor {
        provider = ""remote""

        debug {  
            receive = on 
            autoreceive = on
            lifecycle = on
            event-stream = on
            unhandled = on
        }

    }
    remote {
        dot-netty.tcp {
            hostname = localhost
        }
    }
}

I get the error 'Akka.Configuration.ConfigurationException: ''akka.actor.provider' is not a valid type name : 'Akka.Remote.RemoteActorRefProvider, Akka.Remote''' - as expected. If that helps...

MaximG1234 commented 4 years ago

I might load up the akka source tomorrow myself. I'll let you know any results. Thanks for your help!

ghost commented 4 years ago

Hi @MaximG1234

I've followed your steps and haven't been able to reproduce the issue. Everything works fine for me on OSX using the exact same setup.

However, I don't have the MockDataStore, or InitializeComponent that is shown in your video. Perhaps there is something within that code which is blocking. During the hang, it might help to pause the debugger and view the current threads to see what code is currently executing.

MaximG1234 commented 4 years ago

I have attached the Akka source directly to an example project. The issue is occuring here:

Line 175 Akka.Event.LoggingBus -> AddLogger

askTask.Result blocks:


try
{
    response = askTask.Result;
}
catch (Exception ex) when (ex is TaskCanceledException || ex is AskTimeoutException)
{
     Publish(new Warning(loggingBusName, GetType(),
                        string.Format("Logger {0} [{2}] did not respond within {1} to InitializeLogger(bus)", loggerName, timeout, loggerType.FullName)));
}

The status of askTask is Status = WaitingForActivation

https://www.dropbox.com/s/bywt6cx6k5ejutq/akka.JPG

I am currently in the process of starting up a VM to confirm this is not somehow isolated to my machine.

Aaronontheweb commented 4 years ago

This looks like a runtime problem - that task should time out after 5 seconds by default, but it also shouldn't time out period because by default the only logger we load is the built-in one that writes out to the console.

We saw this sometimes with older versions of .NET Framework - weird TaskCompletionSource behavior that we had to work around using some scheduling hacks and so on. I'm not a Xamarin user, so could you please tell us which version of the runtime you're targeting in it? I.e. a specific Mono version, Android version, etc...

MaximG1234 commented 4 years ago

OK I have now tested on a VM and 100% confirmed reproducible bug.

Android version is 9 API version is 28. I havent tested other versions yet. The Mono.Android.dll referenced is as follows: 10.0.6.2; git-rev-head;c407838; git-branch:d16-3

Attached you will find an example project which reproduces the error.

App1.zip

Aaronontheweb commented 4 years ago

Do you know which Mono version is being used?

MaximG1234 commented 4 years ago

How do I determine that? I thought it was based on the dll Mono.Android.dll?

Aaronontheweb commented 4 years ago

Looking at your XML in the projects, looks like MonoAndroid9.0

Aaronontheweb commented 4 years ago

Sorry, I'm not much of a Xamarin user here - always wanted to learn how but never had a great project for it :p - you'll have to bear with some of my newbie questions while we dig into it.

ghost commented 4 years ago

@MaximG1234

I don't have a Windows machine available right now, but Visual Studio on Mac shows the Mono runtime here by looking at the application settings:

image

Next time I'm on Windows I'll have a look for where the Mono version is specified.

@Aaronontheweb MonoAndroid9.0 sounds like a bridging library, the highest Mono version right now is 6.4.0 I think.

MaximG1234 commented 4 years ago

@Aaronontheweb No worries at all, just grateful that you guys are investigating. This was actually supposed to be my first mobile app but..... :-( lol

@nagytech I am still having trouble determining my version of Mono, I see nowhere in Visual Studio where a version number for Mono is shown and I have tried getting the value using this technique which also seems to be failing (returning null value, looks like method GetDisplayName doesnt exist) .

MaximG1234 commented 4 years ago

Far out, think I finally found it. I believe I was correct in that Mono.Android.dll determines your Mono Version. Microsoft really doesn't seem to make things easy....

"Issues fixed in Xamarin.Android 10.0.6.2 Application Mono Framework behavior on device and emulator This version of Xamarin.Android updates the Mono 6.4 runtime and class libraries from Commit 5608fe0a to Commit 476d72b9, adding 7 new commits."

https://docs.microsoft.com/en-us/xamarin/android/release-notes/10/10.0

ghost commented 4 years ago

@MaximG1234

I've been able to run the code you sent, but something odd is happening.

image

On first startup, everything loads fine. But, if I stop the debugger and run it again I am able to reproduce your issue.

image

Clearing the app cache, force stopping the app, or even uninstalling it won't release the block. Only after shutting down the emulator can I run the application again just fine. But, the second run will always hang.

To me it sounds like a file or resource lock isn't being released when the Xamarin app exits.

If I run adb shell ps before starting up the app, the first time I get:

...
system        4008  1685 1447068  70148 0                   0 S com.android.keychain
u0_a85        4166  1685 1447608  69356 0                   0 S Mono.Android.DebugRuntime

And when the app is started the first time, I see:

...
u0_a85        4166  1685 1447864  68996 0                   0 S Mono.Android.DebugRuntime
u0_a22        4229  1685 1447824  76048 0                   0 S com.android.defcontainer
u0_a88        4318  1685 1564528 144488 0                   0 S com.companyname.app1

Then, when I kill the app, clear the cache and uninstall it I see:

...
u0_a22        6948  1683 1447312  69008 0                   0 S com.android.defcontainer
...

So I think there's something locked or blocked by com.android.defcontainer. Let me have a deeper look and see what's going on.

MaximG1234 commented 4 years ago

@nagytech My bad, I think I might have uploaded a crappy version of my code. Check the file ItemsViewModel in the project and remove the try catch block. Thats probably what is causing your behaviour

MaximG1234 commented 4 years ago

@nagytech After downgrading the project to version 6 of Android I was able to reproduce the intermittent behaviour you describe

ghost commented 4 years ago

@MaximG1234

Looking into the com.android.defcontainer process didn't really uncover anything.

Out of interest, I decided to start up the ActorSystem in a different thread. And, I've not seen the application lock up yet.

        protected override void OnStart()
        {
            Task.Run(() =>
            {
                var s = ActorSystem.Create("client1", ConfigurationFactory.Empty);
                s.Log.Info("Asdf");
            });
        }

So, this leads me to believe that the issue may not be in Akka.NET, but more the thread context which executes the OnStart() method. I'll keep trying to find out more, but the async startup should work for you in the meantime.

MaximG1234 commented 4 years ago

@nagytech Absolute legend. Thanks heaps!

Can confirm this appears to work both in the VS 2019 Android emulator and on my device. Disappointed that I didn't try just running the code in a new thread.

Is there any reason why using the exact code above could cause any issues in the Akka communications? Would the above code be fine to use in production?

ghost commented 4 years ago

@MaximG1234

It's been a long time since I've done any Xamarin development, so I can't say for certain what the best approach would be. From what I recall, it's possible for the OnStart() method to be called multiple times so you might be best to try and instantiate the ActorSystem using a DI framework.

MaximG1234 commented 4 years ago

@nagytech Thanks again for your help!

@Aaronontheweb Just wanted to give you a quick update.

I can confirm that the above solution does successfully work and have now successfully tested a mobile connection and transmitting data.

However, I have found what is likely another possibly related bug. Basically it looks like dispatcher = akka.actor.synchronized-dispatcher isn't being honoured in my application.

I've got some code that looks like this:

public void HandleTablesResponse(IEnumerable<Table> allTables)
{
    this.DisplayAlert("Alert", $"{allTables.Count()} total tables", "cancel");
}

I have successfully been able to get it to work in my WPF app but when I use the same hocon:

 /TableBridgeActor {
    dispatcher = akka.actor.synchronized-dispatcher
}

In my xamarin app unfortunately I get the error

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> Java.Lang.RuntimeException: Can't create handler inside thread Thread[Thread-12,10,main] that has not called Looper.prepare()
  at Java.Interop.JniEnvironment+InstanceMethods.CallObjectMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniMethodInfo method,........

If I change the code to

public void HandleTablesResponse(IEnumerable<Table> allTables)
{
    Device.BeginInvokeOnMainThread(() =>
    {
        this.DisplayAlert("Alert", $"{allTables.Count()} total tables", "hmmmmm?");
    });
}

It successfully executes. I suspect that the framework might not be correctly resolving the dispatcher thread, perhaps because I am starting the ActorSystem on another thread?!

(Sorry for all the bug reports, I love Akka!!! haha) Let me know if you want me to create another issue.

MaximG1234 commented 4 years ago

Hi @Aaronontheweb , I just wanted to quickly followup, while I haven't been able to prove this yet, I think this workaround eventually causes a deadlock in the Xamarin application.

Are you aware of anyone successfully using Akka.NET in a Xamarin project?

ghost commented 4 years ago

Are you aware of anyone successfully using Akka.NET in a Xamarin project?

@MaximG1234 There might be someone in the Gitter channel who has. Try posting your question: http://gitter.im/akkadotnet/akka.net

Aaronontheweb commented 4 years ago

cc @gshackles - he's used it

gshackles commented 4 years ago

I admittedly haven't tried it recently and only really did it in the context of some demo apps, but here's an example of one demo app I've used in the past (a bit of a port of the classic web crawler example): https://github.com/gshackles/akka-samples/tree/master/Mobile/CSharp

This used v1.3.9 (which was current at the time) but did work. I do seem to recall running into issues trying to use remoting/clustering but was able to make use of a local actor system in a Xamarin.Forms app there

MaximG1234 commented 4 years ago

I was able to successfully get a cluster forming connected to a Xamarin Android application. This is some example code that someone else might find useful. I havent tried in iOS yet.

https://www.dropbox.com/s/1u80zkdnu2b8yrg/Xamarin-Cluster-Tester.zip

Just wanted to leave this here for anyone who might attempt to do the same thing.

thzinc commented 4 years ago

I was experiencing this in a Xamarin.Android app when I updated from Akka.net 1.3.3 to 1.3.4. There was a change in the Ask implementation that dropped a ConfigureAwait(false), and the first call to Ask is used in setting up the loggers.

I was able to change the following to create the ActorSystem from:

var actorSystem = ActorSystem.Create("System", config);

to:

var actorSystemTask = Task.Run(() => ActorSystem.Create("System", config)).ConfigureAwait(false);
var actorSystem = actorSystemTask.GetAwaiter().GetResult();
Aaronontheweb commented 4 years ago

Added https://github.com/akkadotnet/akka.net/pull/4424 as a work-around for this issue.