I have some problems with thread synchronization in my Templated control (trying to do a AutoComplete control)
Inside my control I have this code:
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
var searchTextBox = GetTemplateChild("SearchTextBox") as TextBox;
if (searchTextBox != null)
{
var searchDelegate = SearchAsync;
Observable.FromEventPattern(searchTextBox, "TextChanged")
.Select(keyup => searchTextBox.Text)
.Where(TextIsLongEnough)
.Throttle(TimeSpan.FromMilliseconds(500))
.Do(ShowProgressBar)
.SelectMany(searchDelegate)
.ObserveOn(Dispatcher)
.Subscribe(async results => await RunOnDispatcher(() =>
{
IsInProgress = false;
SearchResults.Clear();
foreach (var result in results)
{
SearchResults.Add(result);
}
}));
}
}
And it is complaining that inside my ShowProgressBar method I'm trying to access code that was marshalled by another thread.
If I comment out the Throttle and the ObserveOn(Dispatcher) it works just fine, but it does not throttle my service calls as I want to.
If I only comment out the Throttle part, Nothing happens at all.
Asti's got the right idea, but a far better approach would be to provide the IScheduler argument to Throttle instead:
// NB: Too lazy to look up real name
.Throttle(TimeSpan.FromMilliseconds(500), CoreDispatcherScheduler.Instance)
This will make operations below it happen on the UI thread (including your ShowProgressBar), up until the SelectMany.
Every dependency object requires that any changes to dependency properties be made only on the Dispatcher thread. The Throttle is using a different scheduler, hence making any changes to the UI in a subsequent Do combinator would result in an access exception.
You can resolve this by:
Add an ObserveOnDispatcher before any actions which cause side-effects on the Dispatcher. Optionally use another scheduler down the pipeline.
Use Dispatcher.Invoke to execute side-effects through the dispatcher. E.g., .Do(() => Dispatcher.Invoke(new Action(ShowProgressBar)))
Related
In the below code, the continuation of CreateFileAsync() neither prints nor accesses pdone. However, the zero-length file, Hello.txt, is created.
auto pdone = make_shared<bool>(false);
create_task(folderLocal->CreateFileAsync("Hello.txt", CreationCollisionOption::ReplaceExisting)).then([pdone](StorageFile ^file) {
OutputDebugString(L"In CreateFileAsync continuation!\n");
*pdone = true;
});
create_task([pdone]{
OutputDebugString(L"In my task!\n");
});
create_async([pdone]{
OutputDebugString(L"In my async!\n");
});
while (!*pdone) {}
OutputDebugString(L"Done!\n");
In the debugger:
In my task!
In my async!
I'm not very familiar with debugging WinRT threads yet, but I do not see any obvious exception or any reason the continuation to the async operation should not execute. The target platform is the Hololens emulator.
Any thoughts are appreciated.
Thanks!
Harry's comment above is most likely the culprit - if you initiated this on a UI thread then by default the C++ tasks library (PPL) will try to schedule the completion on the same thread. This will never happen if you are spinning the thread waiting for the completion to happen (classic deadlock).
If you must do this (although you really should try and avoid it) you need to use a "continuation context" to tell PPL to run the continuation somewhere else.
Here's an example. First, basic XAML (just paste inside the Grid of a blank C++ XAML project):
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Content="Hang the UI thread" Click="Hang"/>
<Button Content="Do not do this" Click="DoNotDoThis"/>
</StackPanel>
And the code (just paste after the MainPage constructor):
using namespace Windows::Storage;
using namespace concurrency;
void DoIt(task_continuation_context& context)
{
auto folder = ApplicationData::Current->LocalFolder;
auto done = std::make_shared<bool>(false);
create_task(folder->CreateFileAsync(L"x", CreationCollisionOption::ReplaceExisting))
.then([done](StorageFile^ file) mutable
{
OutputDebugString(L"Done creating file\n");
*done = true;
}, context);
OutputDebugString(L"Going to wait... DO NOT DO THIS IN PRODUCTION CODE!\n");
while (!*done)
;
OutputDebugString(L"Done waiting\n");
}
void MainPage::Hang(Platform::Object^ sender, RoutedEventArgs^ e)
{
OutputDebugString(L"Starting Hang\n");
// The default context == the UI thread (if called from UI)
DoIt(task_continuation_context::use_default());
OutputDebugString(L"Ending Hang\n");
}
void MainPage::DoNotDoThis(Platform::Object^ sender, RoutedEventArgs^ e)
{
OutputDebugString(L"Starting DoNotDoThis\n");
// An arbitrary context will pick another thread (not the UI)
DoIt(task_continuation_context::use_arbitrary());
OutputDebugString(L"Ending DoNotDoThis\n");
}
As noted, you shouldn't do this. If you need synchronous File I/O, and you're accessing files in your own package, use the Win32 API CreateFile2. If you need to access files outside of your package (eg, from a file picker or the photos library) you should use a fully-async programming approach.
I believe using task_continuation_context::use_arbitarty() is the correct way of doing this, however I think microsoft suggests using it slightly differently unless i have misunderstood this link (scroll all the way to the bottom): https://msdn.microsoft.com/en-us/library/hh750082.aspx
create_task(folderLocal->CreateFileAsync("Hello.txt", CreationCollisionOption::ReplaceExisting)).then([pdone](StorageFile ^file) {
OutputDebugString(L"In CreateFileAsync continuation!\n");
*pdone = true;
}, task_continuation_context::use_arbitrary());
There aren't many examples demonstrating indexedDB in a ServiceWorker yet, but the ones I saw were all structured like this:
const request = indexedDB.open( 'myDB', 1 );
var db;
request.onupgradeneeded = ...
request.onsuccess = function() {
db = this.result; // Average 8ms
};
self.onfetch = function(e)
{
const requestURL = new URL( e.request.url ),
path = requestURL.pathname;
if( path === '/test' )
{
const response = new Promise( function( resolve )
{
console.log( performance.now(), typeof db ); // Average 15ms
db.transaction( 'cache' ).objectStore( 'cache' ).get( 'test' ).onsuccess = function()
{
resolve( new Response( this.result, { headers: { 'content-type':'text/plain' } } ) );
}
});
e.respondWith( response );
}
}
Is this likely to fail when the ServiceWorker starts up, and if so what is a robust way of accessing indexedDB in a ServiceWorker?
Opening the IDB every time the ServiceWorker starts up is unlikely to be optimal, you'll end up opening it even when it isn't used. Instead, open the db when you need it. A singleton is really useful here (see https://github.com/jakearchibald/svgomg/blob/master/src/js/utils/storage.js#L5), so you don't need to open IDB twice if it's used twice in its lifetime.
The "activate" event is a great place to open IDB and let any "onupdateneeded" events run, as the old version of ServiceWorker is out of the way.
You can wrap a transaction in a promise like so:
var tx = db.transaction(scope, mode);
var p = new Promise(function(resolve, reject) {
tx.onabort = function() { reject(tx.error); };
tx.oncomplete = function() { resolve(); };
});
Now p will resolve/reject when the transaction completes/aborts. So you can do arbitrary logic in the tx transaction, and p.then(...) and/or pass a dependent promise into e.respondWith() or e.waitUntil() etc.
As noted by other commenters, we really do need to promisify IndexedDB. But the composition of its post-task autocommit model and the microtask queues that Promises use make it... nontrivial to do so without basically completely replacing the API. But (as an implementer and one of the spec editors) I'm actively prototyping some ideas.
I don't know of anything special about accessing IndexedDB from the context of a service worker via accessing IndexedDB via a controlled page.
Promises obviously makes your life much easier within a service worker, so I've found using something like, e.g., https://gist.github.com/inexorabletash/c8069c042b734519680c to be useful instead of the raw IndexedDB API. But it's not mandatory as long as you create and manage your own promises to reflect the state of the asynchronous IndexedDB operations.
The main thing to keep in mind when writing a fetch event handler (and this isn't specific to using IndexedDB), is that if you call event.respondWith(), you need to pass in either a Response object or a promise that resolves with a Response object. As long as you're doing that, it shouldn't matter whether your Response is constructed from IndexedDB entries or the Cache API or elsewhere.
Are you running into any actual problems with the code you posted, or was this more of a theoretical question?
First of all, I'm using the most recent of Rx, that is 2.1. As far as I understand, lots of things have changed when Rx turned 2, so I'm really looking forward to receive an up-to-date answer. Thanks in advance.
I'm implementing a classic task for Rx: observing text of TextBox (AutoCompleteBox from WPToolkit to be exact), in order to provide a list of suggestions to user. Suggestions are fetched from the network, and I want to use these ordinary Rx goodies, like Throttle, DistinctUntilChanged, etc.
I'm also using recently released portable HttpClient for Windows Phone 8, since it provides task-based asynchronous API, which is nice.
The problem I'm having is the cross-thread access while reading the Text value of 'AutoCompleteBox`. Here is the code:
var http = new HttpClient();
var searchFunc = Observable.FromAsync<HttpResponseMessage>(() =>
http.GetAsync(FormatUrl(SEARCH_URL, "DE", new GeoCoordinate(51, 13), searchBox.Text /* <-- causes exception */, 10, "")));
var uithread = new SynchronizationContextScheduler(SynchronizationContext.Current);
var textChange = Observable.FromEventPattern<RoutedEventArgs>(searchBox, "TextChanged")
.Throttle(TimeSpan.FromMilliseconds(800))
.DistinctUntilChanged()
.SubscribeOn(uithread)
.SelectMany(searchFunc)
.Select(async (resp) => SearchResultsParser.ParseSearchResults(await resp.Content.ReadAsStreamAsync(), new GeoCoordinate(51, 13)))
.Select(async (results) => searchBox.ItemsSource = await results)
.ObserveOn(uithread)
.Subscribe();
Exception happens when searchFunc is executed. I see from VS that it executes on a Worker Thread, despite I use SubscribeOn.
Here's the example using SynchronizationContextScheduler, but I've also tried just SubscribeOnDispatcher, with the same result. Looks like I'm missing something important with this ObserveOn stuff, or maybe regarding Observable.FromAsync. Could you kindly point me to my mistake?
SubscribeOn is almost never what you want - you might think it means "Where my Subscribe method runs", but it actually means "Where the actual wiring to the IDisposable (and disposal) runs" - ObserveOn is the equivalent for "This is where I want my actual Subscribe code to execute"
Ref: Observable.SubscribeOn and Observable.ObserveOn
When using the Background Transfer API we must iterate through current data transfers to start them again ahen the App restarts after a termination (i.e. system shutdown). To get progress information and to be able to cancel the data transfers they must be attached using AttachAsync.
My problem is that AttachAsync only returns when the data transfer is finished. That makes sense in some scenarios. But when having multiple data transfers the next transfer in the list would not be started until the currently attached is finished. My solution to this problem was to handle the Task that AttachAsync().AsTask() returns in the classic way (not use await but continuations):
IReadOnlyList<DownloadOperation> currentDownloads =
await BackgroundDownloader.GetCurrentDownloadsAsync();
foreach (var downloadOperation in currentDownloads)
{
Task task = downloadOperation.AttachAsync().AsTask();
DownloadOperation operation = downloadOperation;
task.ContinueWith(_ =>
{
// Handle success
...
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(_ =>
{
// Handle cancellation
...
}, CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled,
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t =>
{
// Handle errors
...
}, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.FromCurrentSynchronizationContext());
}
It kind of works (in the actual code I add the downloads to a ListBox). The loop iterates through all downloads and executes StartAsync. But the downloads are not really started all at the same time. Only one is runninng at a time and only if it finishes the next one continues.
Any solution for this problem?
The whole point of Task is to allow you to have the option of parallel operations. If you await then you are telling the code to serialize the operations; if you don't await, then you are telling the code to parallelize.
What you can do is add each download task to a list, telling the code to parallelize. You can then wait for tasks to finish, one by one.
How about something like:
IReadOnlyList<DownloadOperation> currentDownloads =
await BackgroundDownloader.GetCurrentDownloadsAsync();
if (currentDownloads.Count > 0)
{
List<Task<DownloadOperation>> tasks = new List<Task<DownloadOperation>>();
foreach (DownloadOperation downloadOperation in currentDownloads)
{
// Attach progress and completion handlers without waiting for completion
tasks.Add(downloadOperation.AttachAsync().AsTask());
}
while (tasks.Count > 0)
{
// wait for ANY download task to finish
Task<DownloadOperation> task = await Task.WhenAny<DownloadOperation>(tasks);
tasks.Remove(task);
// process the completed task...
if (task.IsCanceled)
{
// handle cancel
}
else if (task.IsFaulted)
{
// handle exception
}
else if (task.IsCompleted)
{
DownloadOperation dl = task.Result;
// handle completion (e.g. add to your listbox)
}
else
{
// should never get here....
}
}
}
I hope this is not too late but I know exactly what you are talking about. I'm also trying to resume all downloads when the application starts.
After hours of trying, here's the solution that works.
The trick is to let the download operation resume first before attacking the progress handler.
downloadOperation.Resume();
await downloadOperation.AttachAsync().AsTask(cts.Token);
Is it possible to reset a resolved jQuery object to an 'unresolved' state and kick off it's initialization and callbacks all over again?
The specific thing I'm doing is that I have a jQuery deferred wrapper over the local file system api. From there I build up higher level deferreds for the things I care about:
var getFs = defFs.requestQuota(PERSISTENT, 1024*1024)
.pipe (bytes) -> defFs.requestFs(PERSISTENT, bytes)
var getCacheContents = getFs.pipe (fileSystem) ->
defFs.getDirectory('Cache', fileSystem.root).pipe (dir) ->
defFs.readEntries(dir)
Now most of the time, when I call getCacheContents I don't mind the memo-ized values being returned, in fact I prefer it. But, on the occasion when I want to write to the cache I really would like the ability to reset that pipe and have it re-select and rescan the cache next time its accessed.
I could cobble something together from $.Callbacks but a deferred-based solution would really be ideal.
No. A Promise is by definition a thing that resolves only once - from unresolved to fulfilled OR to rejected. You will not be able to do this with jQuery's Deferreds.
What you are actually searching for are Signals. They are to be fired more than once, but provide a similiar interface. There are some implementations around, you might ceck out js-signals or wire.js.
The only solution I could find is to reset the $.Deferred object and return new Promise from that one. It works together with some internal API dirty checking (if something gets edited / deleted), but would be more performant to just reset the existing $.Deferred and let it re-resolve on the next Promise request.
An example of a possible solution is:
$.myDeferredList = [];
$.createRestorableDeferred = function(a,b) {
// JUST BY SIMPLE $.when().then();
$.myDeferredList[a] = {
deferred: $.Deferred()
, then: b
,restore : function() {
$.myDeferredList['myReady'].deferred = $.Deferred();
$.when($.myDeferredList['myReady'].deferred).then($.myDeferredList['myReady'].then);
}
,resolve : function() {
$.myDeferredList['myReady'].deferred.resolve();
}
}
$.when($.myDeferredList['myReady'].deferred).then($.myDeferredList['myReady'].then);
window[a] = $.myDeferredList['myReady'];
}
var counter = 0;
$.createRestorableDeferred('myReady', function () {
console.log('>> myReady WHEN called',++counter);
$.myDeferredList['myReady'].restore();
});
// RESOLVING ways
$.myDeferredList['myReady'].deferred.resolve();
$.myDeferredList.myReady.deferred.resolve();
myReady.resolve();
Results in console:
>> myReady WHEN called 1
>> myReady WHEN called 2
>> myReady WHEN called 3