Ticket Created
over 3 years ago

WERETECH-12052

Conflict/race between makeWebRequest and Storage.setValue called from onUpdate on a watchface.

Hi, I was able to reproduce the following problem (both in simulator and in a Descent MK1 using SDK 3.2.5 and 4.0.6):

If the function setValue is called from onUpdate while makeWebRequest is still waiting for the webserver to return data,

the callback function is "lost" (and not called).

The following code reproduce the problem 100% of the time.



using Toybox.Application;
using Toybox.Background;
using Toybox.System;
using Toybox.WatchUi;
using Toybox.Time;
using Toybox.Communications;
using Toybox.Application.Storage;

(:background)
class PiServiceDelegate extends Toybox.System.ServiceDelegate {
  function initialize() {
    System.ServiceDelegate.initialize();
  }
  function onTemporalEvent() {
    // That URI takes about 3 seconds to answer
    var uri = "https://wb.elaine.fi/pi_slow";
    System.println("Before query");
    var options = {
      :methods => Communications.HTTP_REQUEST_METHOD_GET,
      :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
    };
    Communications.makeWebRequest(uri, null, options,
                                  method(:pi_received));
  }
  function pi_received(code, data) {
    System.println(code);
    System.println(data);
    if (code == 200) {
      Background.exit(data);
    }
  }
}

(:background)
class PiWatch extends Application.AppBase {
  public function initialize() {
    AppBase.initialize();
  }
  function onBackgroundData(data) {
    System.println("onBackgroundData: " + data);
  }
  function getServiceDelegate() {
    return [new PiServiceDelegate()];
  }
  function getInitialView() {
    Background.registerForTemporalEvent(new Time.Duration(5 * 60));
    return [new PiWatchView(), new PiWatchDelegate()];
  }
}

class PiWatchView extends WatchUi.WatchFace {
  public function initialize() {
    WatchFace.initialize();
  }
  public function onUpdate(dc) {
    System.println("onUpdate");
    // On simulator, onUpdate is ran every seconds
    // So we will call Storage.setValue between the http request
    // and the result
    // On watch same problem exists
    Storage.setValue("foo", "bar");
    // Note that problem does not exists if Storage.setValue
    // is called in onPartialUpdate
  }
}

class PiWatchDelegate extends WatchUi.WatchFaceDelegate {
  public function initialize() {
    WatchFaceDelegate.initialize();
  }
}


Here is the output:


onUpdate
onUpdate
onUpdate
Background: Before query
onUpdate
onUpdate
onUpdate
Error: Symbol Not Found Error
Details: Failed invoking <symbol>
Stack:
onUpdate
onUpdate
onUpdate



If I comment the setValue line, I get the expected result:

onUpdate
onUpdate
onUpdate
onUpdate
Background: Before query
onUpdate
onUpdate
onUpdate
Background: 200
Background: {pi=>3.140000}
onBackgroundData: {pi=>3.140000}
onUpdate
onUpdate
onUpdate
Parents
  • As I've said, setValue is expensive, and really not something you want to do all the time in onUpdate, as it will also impact your battery life,  Only call setValue when you need to - do it in onBackgroundDate instead for example.  The most it will be called then will be every 5 minutes.  Or, as I mentioned before you only really need to do it in onStop for the main app,

Comment
  • As I've said, setValue is expensive, and really not something you want to do all the time in onUpdate, as it will also impact your battery life,  Only call setValue when you need to - do it in onBackgroundDate instead for example.  The most it will be called then will be every 5 minutes.  Or, as I mentioned before you only really need to do it in onStop for the main app,

Children
  • What is in your ciq_log file? (Assuming this is on a real device) One thing about the callback you posted for the makeWebRequest, is that it only does an exit if the responseCode==200, which means the backround will run 30 seconds and be killed if you get an error in the call, You'll never see anything in onBackroundData..

    On a real device, the background may not run every 5 minutes, even if that's set for temporal events.  For example, a WF's background won't run the whole time you're using a CIQ device app.

    I've added a setValue() call in onUpdate() in one of my wf's that does comm, and running it the sim as well as on a device, and so far, no issues.

  • Yes, I understand that, but that's not the point.

    The point is if you even call setValue in onUpdate even rarely (not each minute/sec) and you have a http requests waiting in background, it will crash the watchface,

    and there is no way to catch (understand try/catch) it.

    It tooked me very long time to understand what was happening for reasons you can imagine like different http answer time, not calling setValue).

    Traceback are useless in that case, including using the debug xml file.

    I think the problem is worth a look.

    (I know what to do for my watchface ;))

  • of course you are right in real app not this test one.

    there is a lot of work to write good flow to use BAG because there is no choice, you have to use storage to pass dynamic values to BAG (e.g GPS) and save data for future