{"id":92,"date":"2005-02-26T11:30:43","date_gmt":"2005-02-26T09:30:43","guid":{"rendered":"http:\/\/blogs.bl0rg.net\/netzstaub\/?p=92"},"modified":"2005-02-26T11:30:43","modified_gmt":"2005-02-26T09:30:43","slug":"protothreads","status":"publish","type":"post","link":"https:\/\/blogs.bl0rg.net\/netzstaub\/2005\/02\/26\/protothreads\/","title":{"rendered":"Protothreads"},"content":{"rendered":"<p>\n&#220;ber das <a href=\"http:\/\/radio.weblogs.com\/0102385\/\">Weblog von Chris Double<\/a> bin ich heute auf die Multithreading Library <a href=\"http:\/\/www.sics.se\/%7Eadam\/pt\/\">Protothreads<\/a> gestossen, die wirklich sehr interessant ist. Protothreads bietet &#8220;stackless&#8221;-Threads, die man einsetzen kann, um eine Art Poor-Man&#8217;s Multithreading zu haben. Stackless Threads haben keinen eigenen Stack, und m&#252;ssen explizit geschedulet werden, verbrauchen daf&#252;r aber nur 2 Bytes im RAM, und lassen sich mit normalen C-Konstrukten implementieren. Die meisten anderen C-Multithreading-Libraries verwenden daf&#252;r entweder Assembler Code, um z.B. den Prozessorstatus zu sichern oder den Stack zu wechseln, oder andere sehr &#8220;weirde&#8221; C-Konstrukte wie z.B. longjmp und co.\n<\/p>\n<p>\n<strong>Anwendungsbeispiel<\/strong>\n<\/p>\n<p>\nIn der Dokumentation zu Protothreads geben die Entwickler <a href=\"http:\/\/www.sics.se\/~adam\/\">Adam Dunkels<\/a> und Oliver Schmidt, der ihm geholfen hat, 3 Beispiele an. Das einfachste dieser Beispiele ist ein einfacher Thread, der auf das Kippen eines Flags wartet, dann kurz nochmal wartet, und einen anderen Thread umkippt. Code sieht dann so aus:\n<\/p>\n<pre>PT_THREAD(timed_event_thread(struct pt *pt))\n{\n  PT_BEGIN(pt);\n  \n  while(1) {\n    flag1 = 0  \n    PT_WAIT_UNTIL(pt, flag1);\n    timer_set(&#38;timer, TIMEOUT);\n    PT_WAIT_UNTIL(pt, timer_expired(&#38;timer));\n    flag2 = 1;\n  }\n  \n  PT_END(pt);\n}\n<\/pre>\n<p>\n<strong>Lokale Continuations<br \/>\n<br \/><\/strong><br \/>\n<br \/>Protothreads benutzt als unterliegende Struktur &#8220;lokale Continuations&#8221;, um das Multithreading zu implementieren. Klassischerweise ist eine Continuation ein Konstrukt, dass die &#8220;Zukunft&#8221; einer Berechnung festh&#228;lt, wenn ich z.B. die Berechnung &#8220;foobar(a) + blorg(b)&#8221; habe, dann ist die Zukunft der Berechnung foobar(a) in dem Kontext die Berechnung &#8220;+ blorg(b)&#8221;. D.h, wenn foobar(a) ausgef&#252;hrt wurde, muss das Ergebnis mit blorg(b) addiert werden, um die Berechnung abzuschliessen. Diese Continuation kann man in Sprachen wie Scheme z.B. explizit manipulieren, und z.B. irgendwo speichern. Nachdem eine Continuation gespeichert ist, kann man ganz was anderes machen (z.B. zu einem anderen Thread wechseln), und sp&#228;ter dann die gespeichert Continuation wieder aufrufen, und so praktisch einen Thread-wechsel implementieren. Ich hoffe, dass ich das mit einem kleinen Bildchen veranschaulichen kann, ich hab mit Continuations auch echt lange gek&#228;mpft, bis ich das gepeilt habe (und so 100% sicher bin ich mir da immer noch nicht :).\n<\/p>\n<p>\n<a href=\"http:\/\/blogs.bl0rg.net\/netzstaub\/images\/continuation-1.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blogs.bl0rg.net\/netzstaub\/images\/continuation-1-tm.jpg\" height=\"100\" width=\"73\" border=\"1\" hspace=\"4\" vspace=\"4\" alt=\"Continuation-1\" \/><\/a>\n<\/p>\n<p>\nHier sieht man, wie foobar(a) + blorg(b) ausgewertet wird. In Gr&#252;n sind die Schritte, die ausgef&#252;hrt worden sind, in Lila die dazugeh&#246;rige Continuation. Wenn man jetzt diese Continuation abspeichert, und sp&#228;ter wieder aufruft, hat man Multithreading. Bei lokalen Continuations existiert kein Stack per Continuation, so dass die Ergebnisse, die hier in Gr&#252;n sind, explizit in globale Variable abgespeichert werden m&#252;ssen, sonst gehen sie beim Threadswitch verloren. Protothreads bietet 2 Implementierungen von lokalen Continuations in C. Die eine missbraucht das switch Konstrukt, die auf der <a href=\"http:\/\/www.chiark.greenend.org.uk\/~sgtatham\/coroutines.html\">C-Koroutinen-Implementierung<\/a> von Simon Tatham, die andere benutzt die GCC-Compiler Extension f&#252;r &#8220;<a href=\"http:\/\/gcc.gnu.org\/onlinedocs\/gcc\/Labels-as-Values.html\">Labels as Values<\/a>&#8220;. &#220;ber die Implementierung von lokalen Continuations werde ich in einem weiteren Posting bloggen. Eine wichtige Beschr&#228;nkung der Switch-implementierung ist, dass innerhalb der lokalen Continuation kein switch-Konstrukt verwendet werden darf. Die Switch-Implementierung basiert auf so eine Art Trick wie <a href=\"http:\/\/www.jargon.net\/jargonfile\/d\/Duffsdevice.html\">Duff&#8217;s device<\/a>.\n<\/p>\n<p>\n<strong>Protothreads<\/strong>\n<\/p>\n<p>\nWie lokale Continuations sind auch Protothreads in mehreren C-Makros implementiert. Ein Protothread besteht trivialerweise aus einer lokalen Continuation, &#252;ber die die &#8220;Berechnungszukunft&#8221; des Threads festgehalten wird, wenn ein Threadswitch stattfindet. Protothreads werden mit der Makro PT_THREAD deklariert. Das Makro PT_INIT initialisiert die lokale Continuation von dem Thread, und muss vor dem Aufrufen des Protothreads ausgef&#252;hrt werden. Ein Protothread ist also eine einfache C-Funktion, er kann sich auch nicht &#252;ber mehrere Funktionen verteilen, weil das die Implementierung der lokalen Continuations nicht zul&#228;sst. Also, es ist immer noch m&#246;glich aus einem Protothread andere Funktionen aufzurufen, nur d&#252;rfen diese Funktionen keinen Thread-Voodoo machen (keinen Threadswitch ausl&#246;sen).\n<\/p>\n<p>\nInnerhalb der Protothreadfunktion muss das Makro PT_BEGIN benutzt werden (und ihr Pendant PT_END). PT_BEGIN ruft dann die gespeicherte Zukunft des Threads auf. Alles was vor dem Einsatz von PT_BEGIN geschieht wird bei jedem Schedulen des Protothreads ausgef&#252;hrt. Wenn man in das Headerfile &#8220;pt.h&#8221; guckt, sieht man, das PT_THREAD eigentlich eine Funktion mit Returntyp char deklariert. In diesem char wird der Zustand des Threads zur&#252;ckgegeben, also  entweder PT_THREAD_WAITING (der Thread wartet auf irgendwas, und will irgendwann mal wieder aufgerufen werden, um weiter zu machen), oder PT_THREAD_EXITED (der Thread ist fertig). Die Implementierung von PT_WAIT_UNTIL (lass einen anderen Thread laufen, solange eine Bedingung nicht erf&#252;llt ist) ist dann relativ einfach:\n<\/p>\n<pre>#define PT_WAIT_UNTIL(pt, condition)\t        \\\n  do {\t\t\t\t\t\t\\\n    LC_SET((pt)-&gt;lc);\t\t\t\t\\\n    if(!(condition)) {\t\t\t\t\\\n      return PT_THREAD_WAITING;\t\t\t\\\n    }\t\t\t\t\t\t\\\n  } while(0)\n<\/pre>\n<p>\nHier wird die jetzige Zukunft der Berechnung gespeichert (mit LC_SEC), die Bedingung gepr&#252;ft, und wenn diese falsch ist, wird signalisiert, dass der Thread warten will, und bitte ein weiterer an die Reihe kommen soll.\n<\/p>\n<p>\nMit PT_WAIT_THREAD kann man einen Unterthread starten, und warten, bis dieser hier sich komplett ausgef&#252;hrt hat. Jedes Schedulen von dem Oberthread wird gleich wieder zum Unterthread f&#252;hren. Letztendlich kann man mit PT_RESTART die unterliegende lokale Continuation resetten, und somit praktisch den Protothread wieder von vorne laufen lassen. Mit PT_EXIT wird signalisiert, dass der Thread zuende ist. Als Hauptschleife muss man da ein Konstrukt wie dieses hier verwenden (aus example-buffer.c):\n<\/p>\n<pre>int\nmain(void)\n{\n  struct pt driver_pt;\n\n  PT_INIT(&#38;driver_pt);\n\n  while(PT_SCHEDULE(driver_thread(&#38;driver_pt))) {\n    \/*\n     * When running this example on a multitasking system, we must\n     * give other processes a chance to run too and therefore we call\n     * usleep() here. On a dedicated embedded system, we usually do\n     * not need to do this.\n     *\/\n    usleep(10);\n  }\n  return 0;\n}\n<\/pre>\n<p>Besonders interessant d&#252;rften Protothreads auf Embedded-Systemen sein, bei denen man auf Platz achten muss, aber trotzdem sehr oft ein einfaches Multithreadingsystem braucht, um verschiedenen Eingaben zu verarbeiten. Auch interessant d&#252;rfte der Einsatz in Netzwerkservern sein, mal sehen, was man da so machen kann.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>&#220;ber das Weblog von Chris Double bin ich heute auf die Multithreading Library Protothreads gestossen, die wirklich sehr interessant ist. Protothreads bietet &#8220;stackless&#8221;-Threads, die man einsetzen kann, um eine Art Poor-Man&#8217;s Multithreading zu haben. Stackless Threads haben keinen eigenen Stack, &hellip; <a href=\"https:\/\/blogs.bl0rg.net\/netzstaub\/2005\/02\/26\/protothreads\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17],"tags":[],"class_list":["post-92","post","type-post","status-publish","format-standard","hentry","category-computerstaub"],"_links":{"self":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/posts\/92","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/comments?post=92"}],"version-history":[{"count":0,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/posts\/92\/revisions"}],"wp:attachment":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/media?parent=92"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/categories?post=92"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/tags?post=92"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}