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
  • and one more thing, error is in background and it looks like there is on onRecive function see

    https://developer.garmin.com/connect-iq/api-docs/Toybox/System/ServiceDelegate.html

    maybe saving in storage every second is treated by system higher priority so onTemporalEvent is called as delegate function but onRecive doesn't, when I've changed setValue to getValue is ok - less cost.

  • yes, I have the same results. but on f5xPlus (dual cpu too), f6p  - there is no problem.

    however whether or not this bug will be fixed I would not dare to save something in onUpdate or onPartialUpdate.

    I had a lot of problem when in view I read from storage and in app save into storage, so now after many protection I read/save data not in the same time and saving only in 3 places app.onStop, app.onBackgroundData and app.makeReq

  • 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