{"id":55911,"date":"2010-05-03T17:00:00","date_gmt":"2010-05-03T17:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2010\/05\/03\/a-tpl-sandbox\/"},"modified":"2010-05-03T17:00:00","modified_gmt":"2010-05-03T17:00:00","slug":"a-tpl-sandbox","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/a-tpl-sandbox\/","title":{"rendered":"A TPL Sandbox"},"content":{"rendered":"<p><P class=\"MsoNormal\"><FONT size=\"3\" face=\"Calibri\">In a <\/FONT><A href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2009\/10\/27\/9913610.aspx\"><FONT size=\"3\" face=\"Calibri\">previous post<\/FONT><\/A><FONT size=\"3\"><FONT face=\"Calibri\">, we introduced the UnobservedTaskException event, saying that it would be useful for host-plugin scenarios where a host application should continue to execute in the presence of exceptions thrown by buggy plugins.<SPAN>&nbsp; <\/SPAN>A typical example is an Internet browser; should the entire application crash if some rich content plugin crashes?<SPAN>&nbsp; <\/SPAN>Today, we will describe how to achieve this scenario by creating a TPL sandbox using this feature in conjunction with application domains.<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">Application domains (represented by AppDomain objects) were introduced to .NET to provide isolation, unloading, and security boundaries in a single process.<SPAN>&nbsp; <\/SPAN>For example:<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><SPAN>\/\/ Create a PermissionSet for the sandbox AppDomain <\/SPAN><SPAN><\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>PermissionSet<\/SPAN><SPAN> permSet = <SPAN>new<\/SPAN> <SPAN>PermissionSet<\/SPAN>(<SPAN>PermissionState<\/SPAN>.None);<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>&#8230;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>&nbsp;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>\/\/ Create an AppDomainSetup.<\/SPAN><SPAN><\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>AppDomainSetup<\/SPAN><SPAN> setup = <SPAN>new<\/SPAN> <SPAN>AppDomainSetup<\/SPAN>();<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>&#8230;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>&nbsp;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>\/\/ Create the sandbox AppDomain<\/SPAN><SPAN><\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>AppDomain<\/SPAN><SPAN> sandbox = <SPAN>AppDomain<\/SPAN>.CreateDomain(<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN><SPAN>&#8220;Sandbox&#8221;<\/SPAN>,<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN><SPAN>null<\/SPAN>,<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>setup,<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>permSet,<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>CreateStrongName(<SPAN>Assembly<\/SPAN>.GetExecutingAssembly()));<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>&nbsp;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>\/\/ Execute the BuggyPlugin assembly in the sandbox AppDomain.<\/SPAN><SPAN><\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>try<\/SPAN><SPAN><\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>{<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>sandbox.ExecuteAssembly(<SPAN>&#8220;BuggyPlugin.exe&#8221;<\/SPAN>);<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>}<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>catch<\/SPAN><SPAN> (<SPAN>Exception<\/SPAN> e)<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>{<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>LogError(e);<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>}<BR><BR><SPAN>AppDomain<\/SPAN>.Unload(sandbox);<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>&nbsp;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">In the above host code, the BuggyPlugin assembly is executed within a sandbox AppDomain.<SPAN>&nbsp; <\/SPAN>The restricted permissions of the AppDomain prevent the plugin from stepping out of line, and the try\/catch block around the call to ExecuteAssembly handles any synchronous exceptions that propagate from the plugin.<SPAN>&nbsp; <\/SPAN>However, the key word is \u201csynchronous\u201d.<SPAN>&nbsp; <\/SPAN>Asynchronous exceptions, like those thrown within Task bodies, will not propagate through this try\/catch block. <SPAN>&nbsp;<\/SPAN>If left unobserved, they will be treated as unhandled exceptions, tearing down the process by default.<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">The UnobservedTaskException event was added to continue to support this scenario.<SPAN>&nbsp; <\/SPAN>The following code demonstrates how to subscribe to this event by specifying an initializer delegate for the plugin AppDomain.<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><SPAN>AppDomainSetup<\/SPAN><SPAN> setup = <SPAN>new<\/SPAN> <SPAN>AppDomainSetup<\/SPAN>();<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>setup.AppDomainInitializer = _ =&gt;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>{<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN><SPAN>\/\/ Subscribe to the TPL exception event. Unobserved <\/SPAN><\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>\/\/ Task exceptions will be logged and marked as <\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>\/\/ &#8220;observed&#8221; (suppressing the usual exception escalation <\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>\/\/ behavior which would crash the process).<\/SPAN><SPAN><\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN><SPAN>TaskScheduler<\/SPAN>.UnobservedTaskException += <\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN><\/SPAN><SPAN>(<SPAN>object<\/SPAN> sender,&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<SPAN>UnobservedTaskExceptionEventArgs<\/SPAN> exceptionArgs) =&gt;<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>{<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN>LogError(exceptionArgs.Exception);<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/SPAN>exceptionArgs.SetObserved();<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN><SPAN>&nbsp;&nbsp;&nbsp; <\/SPAN>};<\/SPAN><\/P>\n<P class=\"MsoNormal\"><SPAN>};<\/SPAN><\/P>\n<P class=\"MsoNormal\"><BR><FONT size=\"3\"><FONT face=\"Calibri\">With this addition, all unobserved Task exceptions that originate from the plugin code will be logged and marked as observed.<SPAN>&nbsp; <\/SPAN>It\u2019s important to note that there is one of these events per AppDomain.<SPAN>&nbsp; <\/SPAN>This makes it possible to selectively squash Task exceptions from the plugin; exceptions that originate from the host will be treated normally (as serious errors that should stop the application\u2019s execution).<\/FONT><\/FONT><\/P>\n<P class=\"MsoNormal\"><FONT size=\"3\"><FONT face=\"Calibri\">Attached is the full code used in this post.<SPAN>&nbsp; <\/SPAN>Enjoy!<\/FONT><\/FONT><\/P><\/p>\n<p><a href=\"https:\/\/msdnshared.blob.core.windows.net\/media\/MSDNBlogsFS\/prod.evol.blogs.msdn.com\/CommunityServer.Components.PostAttachments\/00\/10\/00\/67\/18\/TPLSandbox.zip\">TPLSandbox.zip<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a previous post, we introduced the UnobservedTaskException event, saying that it would be useful for host-plugin scenarios where a host application should continue to execute in the presence of exceptions thrown by buggy plugins.&nbsp; A typical example is an Internet browser; should the entire application crash if some rich content plugin crashes?&nbsp; Today, we [&hellip;]<\/p>\n","protected":false},"author":485,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7908],"tags":[7907,7911,7909,7912],"class_list":["post-55911","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam","tag-net-4","tag-code-samples","tag-parallel-extensions","tag-task-parallel-library"],"acf":[],"blog_post_summary":"<p>In a previous post, we introduced the UnobservedTaskException event, saying that it would be useful for host-plugin scenarios where a host application should continue to execute in the presence of exceptions thrown by buggy plugins.&nbsp; A typical example is an Internet browser; should the entire application crash if some rich content plugin crashes?&nbsp; Today, we [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55911","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/485"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=55911"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55911\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=55911"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=55911"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=55911"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}