<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Adam Frisby &#187; microthreading</title>
	<atom:link href="http://www.adamfrisby.com/blog/tag/microthreading/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.adamfrisby.com/blog</link>
	<description>ZOMGWTFHAI</description>
	<lastBuildDate>Sat, 26 Dec 2009 07:02:09 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Microthreading .NET</title>
		<link>http://www.adamfrisby.com/blog/2008/11/microthreading-net/</link>
		<comments>http://www.adamfrisby.com/blog/2008/11/microthreading-net/#comments</comments>
		<pubDate>Wed, 26 Nov 2008 06:40:29 +0000</pubDate>
		<dc:creator>Adam Frisby</dc:creator>
				<category><![CDATA[OpenSim]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[lsl]]></category>
		<category><![CDATA[microthreading]]></category>
		<category><![CDATA[Mono]]></category>
		<category><![CDATA[scripting]]></category>

		<guid isPermaLink="false">http://www.adamfrisby.com/blog/?p=85</guid>
		<description><![CDATA[An issue becoming increasingly apparent with the OpenSim platform is that user scripting is a hammer &#8211; it works fine if you only need a couple of them, but lots of them running in conjunction and the operating system begins to start running poorly. The common key to fixing this is to use something such [...]]]></description>
			<content:encoded><![CDATA[<p>An issue becoming increasingly apparent with the OpenSim platform is that user scripting is a hammer &#8211; it works fine if you only need a couple of them, but lots of them running in conjunction and the operating system begins to start running poorly. The common key to fixing this is to use something such as <a href="http://en.wikipedia.org/wiki/Fiber_(computer_science)">Fibres</a> &#8211; small lightweight threads that perform better in mass numbers to full blown Operating System threads.</p>
<p>The problem is &#8211; there&#8217;s no cross platform way to access Fibres, and we have the additional problem that scripts under OpenSim should be transportable from one server to another &#8211; meaning the ability to pack up an execution context, move it to another machine, then resume is a feature we want to support.</p>
<p>Linden Lab&#8217;s <a href="http://jimpurbrick.com/">Jim Purbrick</a> <a href="http://blog.secondlife.com/2006/05/05/microthreading-mono/">wrote in 2006</a> about their work to use a <span style="text-decoration: line-through;">modified version of Mono</span> to achieve this &#8211; unfortunately with OpenSim we do not have the luxury of using modified runtimes, nor do we plan on dropping support for the official .NET runtime either. This means solutions such as <a href="http://www.bat.org/~tomba/monoco.html">Mono Continuations</a> dont work for us very well.</p>
<p>(<em>Update 26/11/08: I had a chat with Jim after showing him this article &#8211; apparently Linden is doing something fairly similar to this proposal already, without the aid of a modified runtime.</em>)</p>
<p>The solution, is ideally something we can run on both .NET and Mono, runs reasonably quickly (but obviously some overhead is acceptable) &#8211; it needs the ability to save and restore the current execution state, and it needs the ability to suspend and resume. Suspension and resumation should be fairly fast to allow it to be used in a microthreading context.</p>
<p>These requirements unfortunately knock out a very large number of options &#8211; the options we considered ranged from using IEnumerator and &#8216;yield&#8217; to handle threading (no save/restore or return values), to using reflection to emulate an instruction pointer and stack (too slow and complex).</p>
<p>The idea I am currently developing manages to avoid most of these problems, but requires a degree of runtime code mangling &#8211; the result however is something that is generally applicable to generic .NET applications that desire save/resume functionality (or microthreading).</p>
<p>The result centers around Mono.Cecil &#8211; the runtime analysis library written by one of the Mono developers. It has a very nifty feature however in that it is capable of changing a CIL stream, then writing back out the resulting assembly &#8211; meaning we can at the CIL level, begin to manipulate the results and hook in accessory functions to handle our save/restore worries.</p>
<h3>Shadowing Variables</h3>
<p>The first thing we need to consider trying to save the state of an in-execution script is that unlike Java, C# doesnt have a <strong>MethodInfo.LocalVariables.Get()</strong> method &#8211; to the C# programmer, the current Method&#8217;s stack is completely inaccessible. It <em>does however</em>, have the ability to list the local variables within this method.</p>
<p>And that&#8217;s where we enter the shadow variables &#8211; the goal of the shadow variables, is to dynamically build a Struct that contains a corresponding entry for each local variable within the method, at runtime we use Cecil to swap references to the local variables, to references to the Shadow equivilent &#8211; then saving and restoring becomes as simple as serialising and deserialising the shadow container.</p>
<p>Internally, the shadow container should be created before the method call is begun and passed in as an argument &#8211; for something that isnt resuming, it would simply be containing null values.</p>
<h3>Resuming Execution</h3>
<p>The next consideration is the ability to save and resume from an execution point within the code &#8211; this is handled through gratuitous use of the CIL equivilent of a &#8216;GOTO Label&#8217;. After each statement, we place a new label &#8211; and at the beggining of the method call, we also add a new argument (&#8221;InstructionPointerStartPoint&#8221;) and a switch statement that will &#8216;goto&#8217; the appropriate label.</p>
<p>Since we&#8217;re passing the shadow container in via the arguments &#8211; we do not need to do anything special in terms of copying data back, as all the code references the shadow container directly &#8216;as is&#8217;. This basically emulates the instruction pointer within a standard x86 processor.</p>
<h3>Handling Return Values and the Stack</h3>
<p>The final consideration we need to make is recursive calls. Suspendable user methods are allowed to call other suspendable user methods &#8211; so we need to implement our own call stack manager to make sure we can save the context of a previous call leading to the current one. (Otherwise resumption would effectively fail).</p>
<p>The solution to this is thankfully fairly simple &#8211; we create a new &#8216;StackItem&#8217; class which contains: A reference to the virtual instruction pointer (where we are currently in the execution), a reference to the function call for this method, and a reference to the current shadow container, as well as an optional reference to a decendant stack item for nested calls.</p>
<p>Before proceeding into a nested call, we need to create a new stackitem for it, and launch it within that execution context, but otherwise it falls together fairly simply.</p>
<p>By folding the stackitems together from the initial execution context, we&#8217;re able to then package up a copy of the running execution at any single point in time, transport it, then bring it back later. The whole thing ends up looking something like this.</p>
<p><img src="http://www.gwala.net/opensim_microthread.png" alt="How it all looks" width="475" height="375" /></p>
<h3>Optimising</h3>
<p>Under this situation, we do have some fairly expensive penalties when it comes to calling into a recursive function, but at the same point there are a number of relatively easy fixes. Functions for example can be inlined &#8211; only a very tiny number of functions (such as self-recursive functions) need to be handled fully. The vast majority can be inlined, thereby reducing the need to setup nested function calls in a large number of cases.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.adamfrisby.com/blog/2008/11/microthreading-net/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
