question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Infinite loop when messages are sent too quickly in succession

See original GitHub issue

Updated with workaround

Applies to: Android (maybe iOS)

Description: To update the model along with what the user does, I listen to events like TextChanged. This works fine. But under some circumstances, such event might trigger an infinite update-view loop which will freeze the app. Based on what I do and the logs, I suspect EXF doesn’t like TextChanged to trigger too quickly in a row. I will update this issue when I find more about this.

iOS seems not to be affected.

Logs In ElmishContacts, I searched for a contact “Hoya Booya” and then erased my search.

Message: MainPageMsg (TabAllContactsMsg (UpdateFilterText "Ho"))
Updated model: ...
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
View, model = ...
View result: NavigationPage(...)@821839065
Updating NavigationPage, prevCount = 1, newCount = 1
Adjust page number 0
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Image
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
Create Xamarin.Forms.Label
Create Xamarin.Forms.Image

Message: MainPageMsg (TabAllContactsMsg (UpdateFilterText "H"))
Updated model: ...
View, model = ...
View result: NavigationPage(...)@571913183
Updating NavigationPage, prevCount = 1, newCount = 1
Adjust page number 0
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label

Message: MainPageMsg (TabAllContactsMsg (UpdateFilterText ""))
Updated model: ...
View, model = ...
View result: NavigationPage(...)@936190227
Updating NavigationPage, prevCount = 1, newCount = 1
Adjust page number 0
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Image
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
Create Xamarin.Forms.Label
[IInputConnectionWrapper] beginBatchEdit on inactive InputConnection
[IInputConnectionWrapper] getTextBeforeCursor on inactive InputConnection
Create Xamarin.Forms.Image

Message: MainPageMsg (TabAllContactsMsg (UpdateFilterText "H"))
Updated model: ...
View, model = ...
View result: NavigationPage(...)@906179063
Updating NavigationPage, prevCount = 1, newCount = 1
Adjust page number 0
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label

Message: MainPageMsg (TabAllContactsMsg (UpdateFilterText ""))
Updated model: ...
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
View, model = ...
View result: NavigationPage(...)@765267125
Updating NavigationPage, prevCount = 1, newCount = 1
Adjust page number 0
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Image
[IInputConnectionWrapper] getTextAfterCursor on inactive InputConnection
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
Create Xamarin.Forms.Label
Create Xamarin.Forms.Image

Message: MainPageMsg (TabAllContactsMsg (UpdateFilterText "H"))
Updated model: ...
View, model = ...
View result: NavigationPage(...)@991281053
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
Updating NavigationPage, prevCount = 1, newCount = 1
Adjust page number 0

Message: MainPageMsg (TabAllContactsMsg (UpdateFilterText ""))
Updated model: ...
View, model = ...
View result: NavigationPage(...)@52470701
Updating NavigationPage, prevCount = 1, newCount = 1
Adjust page number 0
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Image
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
Create Xamarin.Forms.Label
[IInputConnectionWrapper] getSelectedText on inactive InputConnection
Create Xamarin.Forms.Image

Message: MainPageMsg (TabAllContactsMsg (UpdateFilterText "H"))
Updated model: ...
View, model = ...
View result: NavigationPage(...)@594339090
Updating NavigationPage, prevCount = 1, newCount = 1
Adjust page number 0
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label

Message: MainPageMsg (TabAllContactsMsg (UpdateFilterText ""))
Updated model: ...
View, model = ...
View result: NavigationPage(...)@1068286510
Updating NavigationPage, prevCount = 1, newCount = 1
Adjust page number 0
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
[IInputConnectionWrapper] endBatchEdit on inactive InputConnection
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Image
Create Xamarin.Forms.StackLayout
Create Xamarin.Forms.Label
Create Xamarin.Forms.Label
Create Xamarin.Forms.Image
[IInputConnectionWrapper] beginBatchEdit on inactive InputConnection
[IInputConnectionWrapper] getTextBeforeCursor on inactive InputConnection

// And on, and on...

Repro: https://github.com/TimLariviere/EXF_ReproInfiniteLoop

  1. Start the repro
  2. Type some text slowly in the Entry field (like you would normally)
  3. Working!
  4. Start mashing your keyboard (write quickly) or erase the field
  5. Not working. EXF goes berserk.

Workaround: The issue only occurs when the UI updates quicker than EXF follows, so one way to prevent that is to throttle the TextChanged event and only send a single message when no more TextChanged are triggered for a given time.

Here’s an example with a 250ms timeout.

One important point: we need to “fix” the throttle function with fixf => fixf throttle dispatch 250. The throttle func start a new loop each time it is called, and without caching it with fixf, a new loop would be created each time the view refreshes.

let throttle fn timeout =
    let mailbox = MailboxProcessor.Start(fun agent ->
        let rec loop lastMsg = async {
            let! r = agent.TryReceive(timeout)
            match r with
            | Some msg ->
                return! loop (Some msg)
            | None when lastMsg.IsSome ->
                fn lastMsg.Value
                return! loop None
            | None ->
                return! loop None
        }
        loop None
    )
    mailbox.Post

let view (model: Model) dispatch =
    let throttledDispatch = fixf throttle dispatch 250

    View.ContentPage(
      content = View.StackLayout(
        children = [
            View.Entry(
                text=model.TextWorkaround,
                textChanged=(fun e -> throttledDispatch (TextChangedWorkaround e.NewTextValue)))
        ]))

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:15 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
TimLarivierecommented, Nov 5, 2018

Ah yes indeed. I think the easiest way to solve this would be to extract the condition from the event handler. Losing focus won’t happen as often as TextChanged, so you should be fine dispatching NameFocusLost everytime.

let update msg model =
     match msg with
     | NameFocusLost text ->
         if model.Name <> text then
             { model with Name = text }, (Cmd.ofMsg NameChanged)
         else
             model, Cmd.none
     | NameChanged ->
        // Do something on name changed

let view model dispatch =
     View.Entry
         (text = model.Name, 
          created = (fun entry ->
              entry.Unfocused.Add(fun args -> dispatch (NameFocusLost entry.Text))))
1reaction
kevinhacommented, Nov 2, 2018

@willsam100 since it is now possible to capture the control we can do this:

View.Entry
    (text = model.Name, 
     created = (fun entry -> 
         entry.Unfocused.Add(fun args -> 
             if model.Name <> entry.Text then dispatch (NameChanged entry.Text))))

Are there any downsides to this, apart from having to access the underlying control? Is it better not to compare the model and control values?

The Unfocused event has been around since at least XF3.1. Is there a particular reason it is not in Fabulous?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Infinite loop when messages are sent too quickly in ...
Based on what I do and the logs, I suspect EXF doesn't like TextChanged to trigger too quickly in a row. I will...
Read more >
Multiple messages to the bot in quick succession crash it
I have a a bot that runs on .NET + Bot Framework + Azure + Facebook Messenger. Initial Problem. I was trying to...
Read more >
Solved: Infinite Loop in Flow - Power Platform Community
it means that it will modify the items again, even though it has been modified. This will caused the infinite loop to happens....
Read more >
interrupt handling in infinite loop - TI E2E - Texas Instruments
I am currently working on 'Face Recognition' which requires LCD to be interfaced with the MSP430G2553. I have to display a message repeatedly....
Read more >
V663. Infinite loop is possible. The 'cin.eof()' condition ...
Infinite loop is possible. The 'cin.eof()' condition is insufficient to break from the loop. Consider adding the 'cin.fail()' function call to the conditional ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found