Awesomium multithread
Here is the code I currently use to get the whole page screenshot:
public RenderBuffer GetRender(string url,
SynchronizationContext synchronizationContext)
{
WebView webView = null;
synchronizationContext.Send(
(o, s) => webView = WebCore.CreateWebView(1024, 768), null);
synchronizationContext.Send(((o, s) => webView.LoadURL(url)), null);
while (webView.IsLoadingPage)
SleepAndUpdateCore(synchronizationContext);
webView.RequestScrollData();
webView.ScrollDataReceived += OnScrollDataReceived;
while (!_finishedScrollDataReceived)
SleepAndUpdateCore(synchronizationContext);
webView.Resize(_scrollData.ContentWidth, _scrollData.ContentHeight);
while (webView.IsResizing)
SleepAndUpdateCore(synchronizationContext);
return webView.Render();
}
private void OnScrollDataReceived(object sender, ScrollDataEventArgs e)
{
_finishedScrollDataReceived = true;
_scrollData = e.ScrollData;
}
private void SleepAndUpdateCore(SynchronizationContext synchronizationContext)
{
Thread.Sleep(100);
synchronizationContext.Send((object state) => WebCore.Update(), null);
}
This code should be executed by 10 threads. The problem is I have no idea which methods are thread safe and which are not. I had to synchronize LoadUrl method (in other case I got CorruptedMemoryException) which I guess will make my application almost one-thread (as url retrieval is the longest part). Is it by design?
I am using the Awesomium 1.6.2 and the last AwesomiumSharp.
Comments are currently closed for this discussion. You can start a new one.
Support Staff 2 Posted by Perikles C. Stephanidis on 01 Sep, 2011 03:33 PM
Hello,
Currently, types and their members in Awesomium and AwesomiumSharp are not thread-safe. It is also stated in the documentation (see Thread Safety section here but also in many other topics of interest). So this means that yes, all calls to any member in Awesomium and AwesomiumSharp, should be made from the thread that initiated the WebCore. It does not need to be a UI thread (although it's obvious that it has to be if you plan to render the produced buffer on UI and not just save it) as long as all calls keep coming from the same thread thoughout the lifetime of the WebCore and any web views created. Perhaps it was not made as clear as it should be and we apologize for that.
Note that in AwesomiumSharp 1.6.2, if the WebCore is initiated from a UI thread, the necessary synchronization context is created internally and auto-update is enabled (see
WebCore.IsAutoUpdateEnabled).There are plans bringing multi-threading back in later releases of Awesomium and AwesomiumSharp.
Regards,
Perikles
3 Posted by alexidsa on 01 Sep, 2011 05:50 PM
But according to my example, some operations are thread-safe: as you can see, I don't wrap all WebCore/WebView calls to SyncronizationContext and it works... So I guess you mean you don't guarantee it is stable, yes?
The main goal for me is to make url retrieval out of single (UI) thread. Do I achieve this goal by my code. webView.LoadUrl is asynchronous and is executed very quickly. And after that I'm waiting till page is downloaded in secondary thread. But I'm not sure in which process url is really retrieved.
Sorry, I didn't get your point about IsAutoUpdateEnabled. How can I use it and what will it give to me?
Support Staff 4 Posted by Perikles C. Stephanidis on 01 Sep, 2011 09:32 PM
Hello,
You are right. Not thread-safe means it is not safe to call any member from a background thread. This doesn't mean that every call will necessarily fail. Some calls may occasionally pass through, but you should not rely on this. The current status is that Awesomium is not thread-safe. This has to do with how Awesomium (the native code) is implemented. It is not an AwesomiumSharp (.NET wrapper) issue.
The event you should handle for your purpose is:
WebView.LoadCompleted. But before this, let me explain auto-update:WebCore.Update. The WebCore internally obtains a valid synchronization context from the UI thread and starts internal auto-update, that is performed in intervals indicated by theWebCore.AutoUpdatePeriodproperty (the default is every 20 milliseconds). Then, if what you want to do is actually draw theRenderBufferon a surface, you simply monitor theWebView.IsDirtyChangedevent. You can read the Remarks section in the documentation ofWebCore.Update.WebCore.Initialize) somewhere else in your application before you callGetRender, then the core is automatically initialized (with default configuration settings), when you create your first view. Since in your case this happens within aSynchronizationContext.Sendcallback, then your WebCore is initialized in the UI thread and this means that you already get auto-update.In your case, it appears that you simply want to save a snapshot of the full page. Given my description above, you cannot achieve this by having multiple threads concurrently navigating to different URLs and getting images, neither can you achieve this completely synchronously, without blocking your UI thread. You need to rely on WebView events (to avoid blocking the UI) and only save one image at a time, possibly canceling a previous operation that has not completed yet, when a new URL request arrives, or, you can create a separate view for every request (as you already do). This however may be memory-consuming depending on how many images you try to save within a certain period.
So, let me summarize all these by giving you a sample that should do what you try to do. In this sample I will demostrate both scenarios:
For the sample I will use a WinForms Form:
Regards,
Perikles
Perikles C. Stephanidis closed this discussion on 13 Sep, 2011 11:33 AM.