Talk:Duff's device/Archive 1

Tom himself
Here is a fresh comment from Tom himself why he wrote it on the Lambda weblog (nexus of the sect of functional programming :-):

What was I reading to write that? I knew enough to write that bit of code because I had read the source code for Dennis Ritchie's PDP-11 C Compiler, specifically the version shipped with Fifth Edition UNIX. It's a remarkable program -- it compiles a fairly sophisticated language, producing decent code, all in a 64K byte footprint. Most of the syntax analysis is done by recursive descent, except for expressions, which are parsed by using precedence tables with what we called the Railway Siding algorithm when I was in school. Code generation is done by a table-driven tree-pattern matcher. The whole compiler runs in two passes (uhh, actually seven if you count one for the preprocessor, one for the optional freestanding peep-hole optimizer and three for the assembler), but the organization is essentially single pass -- it was split in two for space reasons. The first pass does syntax analysis & typechecking and generates code for everything but expressions. (And maybe switch statements? It's been 30 years since I read the code.) Its output is an assembly-language code/data stream with embedded expression trees to be processed in the second pass. If the machine it compiled for were still of contemporary interest, I'd recommend it to anyone looking for a good introduction to the practicalities of software design and implementation. Presumably to keep the compiler's size managable, case labels are treated syntactically as ordinary goto labels with a number attached. There was a stack of switch statements, and whenever a case was encountered, a label went in the output and was recorded in the stack (with an error if the stack was empty, i.e. if no switch was active.) Since case labels weren't syntactically bound to switch statements, they could occur anywhere in the switch's body, including, for example, in the middle of a nested do-while statement. By Tom Duff at Wed, 11/23/2005 - 17:54

Here is Tom's comment

--Marc van Woerkom 15:12, 2 December 2005 (UTC)

Missing information
There is no explanation in the text on why the pointer increment is missing from "to". —The preceding unsigned comment was added by Skypher (talk • contribs) 15:54, 28 January 2007 (UTC).
 * The canonical example of this is copying to memory-mapped I/O. —The preceding unsigned comment was added by 71.245.136.42 (talk) 06:18, 9 February 2007 (UTC).


 * There's nothing "canonical" about Duff's original example; note that the Duff's Device given in the article isn't even the same code as in his original email. The non-incrementing of to in his case is irrelevant to the working of the device and deserves at most a footnote, not its current confusing treatment as if it were a major issue (the fact that there's a library routine to copy isn't relevant, because there are many other ways to use the device that won't be found in a library routine, e.g. *to++ = func(*from++)).


 * Yes, there is:
 * Note that to is not incremented because Duff was copying to a single memory-mapped output register.
 * Kurosa 01:39, 22 February 2007 (UTC)

I really do not understand why keep this requirement of (count > 0) - if you rewrite it a bit, it can be zero:
Oops, made a mistake :)

/* this assumes count is signed */ switch (count & 7) { while ((count -= 8) > -1) { *d = *s++; case 7:	*d = *s++; case 6:	*d = *s++; case 5:	*d = *s++; case 4:	*d = *s++; case 3:	*d = *s++; case 2:	*d = *s++; case 1:	*d = *s++; case 0: ; } }

Best regards, Ashot.


 * Put a better explanation for non-C programmers please...


 * I'm not so sure that a non-C programmer would get a lot of excitement/revulsion out of this --kibibu 01:15, 7 October 2005 (UTC)


 * The sort of explanation for a non-C programmer would be useful for those just starting to program in C as well. Also people wondering about this as the basis of coroutines and protothreads. Hackwrench 02:17, 7 October 2005 (UTC)


 * The only thing a non-C programmer needs to understand this article is a beginner's knowledge of C, which can be obtained the usual way and doesn't belong here. This holds even more so for understanding C implementations of coroutines and protothreads that use the same trick that Duff's Device uses (they do not use Duff's Device per se; as Duff wrote in his original email, "I have another revolting way to use switches to implement interrupt driven state machines [...]"). As for the rewrite, it adds an extra subtract, test, and branch for any multiple of 8, so it would be slightly more efficient to test for count == 0 as a special case -- which often isn't needed (when it is known that count can't be 0). -- Jibal 08:21, 6 April 2007 (UTC)


 * Your code does not compile. Code cannot exist in a switch-case before the first case-label. Adding a dummy "case -1:" there would work though. Ps. Please sign your comments with ~ . Bisqwit 13:28, 24 August 2007 (UTC)
 * I don't know what compiler you used but the code compiles just fine on my g++ compiler. The dummy is was not needed on this compiler. 8 September 2007

Missing Book
K&R's The C Programming language should probably be added to the list of books, since Stroustrup's is there. SavantEdge 23:43, 26 April 2007 (UTC)
 * I agree. I added it. Jeff Carr (talk) 05:57, 19 November 2007 (UTC)

One usage of the device is not listed
Apart from being used in the context of writing to a device. The device is also used to test C compilers and C++ compilers. Essentially, if the compiler freaks out on the code it isn't a true C or C++ compiler.

Also, as others said, there are other uses of the device other than writing to device. The *to++ = func(*from++) example is as such a more general use of the device.

I also think one should be explicite about telling that the switch is done outside of the loop. It seems that many people misses this detail - if the switch is done inside the loop you make the test each time and gain nothing and also it is not duff's device any more.

Alf —Preceding unsigned comment added by 217.144.227.22 (talk) 11:30, 9 January 2008 (UTC)

Irreducible flow graph?
Doesn't Duff's Device give you an irreducible flow graph, so that an optimizing compiler would most likely split it, giving one the same code as one would get handling the count % 8 bytes outside the loop? —Preceding unsigned comment added by 209.234.91.82 (talk) 15:59, 31 January 2008 (UTC)

Modern compilers
Shouldn't modern compilers already have some kind of loop unrolling optimization like this already?

I don't see any advantage of using this device, other than for historic reason.--Huntercool (talk) 10:37, 6 June 2008 (UTC)


 * Duff's Device targets a very special problem (serial copy to a register) that modern compilers are unlikely to have an optimization for. That said, the device itself is probably not useful for its original purpose, but the technique is still applicable. For example, it is possible to implement protothreads in pure C with the same nested switch/case construct. Rrelf (talk) 11:28, 11 June 2008 (UTC)

Compiler warnings
When I compile the example source on a linux box, I get:

% gcc -O2 -Wall -o duff-orig duff-orig.c duff-orig.c:6: warning: return type of 'main' is not 'int' duff-orig.c: In function 'main': duff-orig.c:9: warning: implicit declaration of function 'atoi' duff-orig.c:10: warning: implicit declaration of function 'send' duff-orig.c: At top level: duff-orig.c:14: warning: conflicting types for 'send' duff-orig.c:10: warning: previous implicit declaration of 'send' was here

Therefore I would make the following suggestions:


 * Fix main's return type, add a "return 0".
 * Add an include for stdlib.h for the atoi prototype.
 * Rename send to something not already used, like duff.
 * Add a prototype for duff above main.

The result compiles cleanly, here it is. Do these changes detract from the readability of the example?

It also seems a bit weird that the indentation for duff and main don't match. Infinoid (talk) 13:03, 11 July 2008 (UTC)

slashdot
This article has been linked from slashdot, so I just added the slashdotted template as a precaution. --Codemonkey 01:07, 7 October 2005 (UTC)

The mod 8 operation could be optimized at the cost of readability. Mod is a complex operation, and unless the compiler optimizes for the specific case of mod $$2^n$$ likely any performance improvements seen by using a jump table will vanish.

It is more optimal to use a logical and for the modulus and a logical shift for the division.

This also illustrates the importance of maintaining a length that is a power of 2.

--67.173.214.71 02:09, 7 October 2005 (UTC)


 * What do you mean that mod is a complicated operation? It comes free with every assembly DIV instruction. Hackwrench 02:29, 7 October 2005 (UTC)


 * Also why would the remainder be a loop? Hackwrench 02:29, 7 October 2005 (UTC)


 * Re: "[Mod] comes free with every assembly DIV instruction." Counting instructions can be misleading.  On most processor implementations, not every instruction is born equal.  For instance, on an Intel Pentium 4 processor, the DIV instruction takes 80 cycles, while a mask operation takes only 1 cycle (note: mod $$2^n$$ can be implemented as a mask operation, i.e., using the AND instruction).  In other words, on an Intel Pentium 4, if you know that the divisor is a power of 2, your implementation of the modulus operation can be 80 times faster than the general case where nothing is known about the divisor and where you must fall back to use the DIV instruction.  [A source for the instruction latencies is http://swox.com/doc/x86-timing.pdf].


 * Re: "Duff's Device is an optimized implementation of a serial copy." Tom Duff's device is not just about optimizing serial copy.  It's more general than that.  It's a way to express loop unrolling (or loop unwinding) in the C programming language.  Optimizing serial copy is just one application of loop unrolling, there are many others.  See point 1 of Tom's original usenet posting (available for instance there http://www.lysator.liu.se/c/duffs-device.html).


 * Re: "Based on an algorithm used widely by assemblers..." Loop unrolling is also implemented in many compilers (e.g., GNU's GCC).  Since fewer and fewer folks write assembly anymore, loop unrolling is perhaps now more likely to be the result of a compilation.


 * If you're interested in optimization, the best thing you can do is throw any compiler that doesn't optimize for mod $$2^n$$ into the trash and use a decent one; writing less readable code to help out a non-optimizing compiler is penny wise and pound foolish. As for "maintaining a length that is a power of 2", Duff's Device handles any length (except 0); 8 is just the block factor for unrolling, which should indeed be a power of 2. Also, the jump table is a result of the case values being a non-sparse set of constants; it has nothing to do with powers of 2. Finally, Duff's Device does not obtain a performance improvement over a more straightforward loop unrolling implementation that uses an 8-at-a-time loop plus a switch statement for the leftover elements (the way Duff said he would "usually handle this"), it only reduces the number of lines of code by combining the loop and the switch statement. -- Jibal 08:47, 6 April 2007 (UTC)


 * Foolish? I learned long ago not to assume anything about what a compiler can and cannot optimize (hint: check the generated assembly code every once in a while to get a feel for what things cost.) In this case the function is taking a signed size for some reason, and on virtually all C compilers division/modulo round towards zero whereas shift/and round towards negative infinity. At best this means that the compiler might use some clever bithack to adjust the shift/and, and at worst (surprisingly often in fact) it won't bother and resort to a plain old division.--83.166.6.116 (talk) 22:05, 1 October 2008 (UTC)

Removing content
There currently are two source-code variants of the same concept in the article. The similarity between the two is very high, so i'm thinking about merging them to leave only one snippet. However, before i do such a thing, i'd like some feedback about it.

Also, there's this: "Its behavior is undefined if it is entered with count having the value zero.", with which i disagree. The behaviour is quite predictable: it will run a loop of roughly MAXINT iterations, copying the memory contents starting with *from... -- Jokes Free4Me (talk) 10:58, 11 June 2008 (UTC)
 * As far as I'm concerned, the second variant (the one that starts with "duff.c" in a comment) is entirely redundant and can be safely removed. Rrelf (talk) 11:25, 11 June 2008 (UTC)


 * I have removed the part with duff.c because:
 * 1) it is unsourced or OR
 * 2) This article is about the code that Tom Duff wrote and named, not about other, purportedly similar codes.
 * 3) The last part of it reads like a manual.
 * 4) It talks about fixing the count == 0 bug, but the original code was clearly described as not working for count == 0, so this is not a bug.
 * Lklundin (talk) 19:58, 20 November 2008 (UTC)

C-FAQ.com explanation
This is another comprehensive explanation: http://c-faq.com/misc/duffexpln.html —Preceding unsigned comment added by Parallelized (talk • contribs) 13:50, 15 February 2009 (UTC)

legal C by virtue of the relaxed specification of the switch statement
I know this may seem heretical, but to me the reason Duff's device is non-obvious is because of the relaxed specification of the While statement: it is legal to jump into the centre of a While statement, and the Switch statement is used as a way of adding the jump-to-the-middle.

Look at it again: What is special about the switch statement? Nothing. What is special about the While statement? It has a jump to the middle.

The idea of structured programming was that programming could be done using structures, where a structure was something like a "function" or an "object". Using these higher level structures would make programming faster and more correct.

A structured programming language was a programming language that had structure built in. Just like it is possible to do object-oriented programming in a non-object oriented language (the first versions of C++ used the C compiler and a macro language), it is possible to do structured programming in a non-structured language: you construct "while" loops out of "goto" statements.

But the point of structured programming is to use the structures as fixed ideas. In particular, Do,While,For are supposed to have single, fixed entry and exit points. Duff's device subverts our expectations of the While loop by not entering the While loop at the While.

I don't see that it subverts our expectations of the Switch statement in any way. —Preceding unsigned comment added by 203.206.162.148 (talk) 07:37, 11 December 2009 (UTC)


 * Agreed. There are two things here that may be unintuitive for programmers: fall-through between switch cases and jumping into the middle of a loop. Of these two, the first is well-known to every C programmer while the second is far less so, and is what made me surprised by the device. 84.108.59.222 (talk) 12:07, 1 February 2010 (UTC)

memcpy is not equivalent
In case of overlapping to and from areas, memcpy is not equivalent. overlapping copies are often used in (lzss-based) compression algorithms. 109.192.116.219 (talk) 09:41, 1 June 2010 (UTC)
 * memcpy is not equivalent to this code EVER. This code copies from an array of several memory locations to a single location, where memcpy copies from one array to another. 75.137.114.54 (talk) 05:53, 23 May 2011 (UTC)

"volatile" keyword needed?
Isn't it necessary here to declare "to" as a pointer to a volatile short? Otherwise, I'm thinking that an optimizing compiler, seeing that the value of "to" never changes, could generate code that only performed a single store into "*to" for each call to the send function. That optimization would be appropriate behavior for "to" being a pointer to a RAM location, but not for "to" being a pointer to a memory-mapped I/O device. &mdash; Wdfarmer (talk) 02:39, 15 May 2011 (UTC)

This is true for most modern compilers, but the original code was targeted at a 1980s compiler that didn't do anywhere near as much optimization as modern ones do. As such, the volatile keyword didn't exist, any memory reference resulted in an actual memory reference. 75.101.123.180 (talk) 10:24, 9 June 2011 (UTC)

Historically speaking...
From the article: The primary increase in speed versus a simple, straightforward loop comes from loop unwinding, which reduces the number of branches performed (which are computationally expensive due to the need to flush - and hence stall - the pipeline).

Did CPUs even have pipelines in 1983? —Preceding unsigned comment added by 75.186.5.185 (talk) 08:05, 31 March 2011 (UTC)


 * Even the earliest SPARCs come a couple years afterwords, but this portion of text is from a modern perspective. There is a rather more serious problem with this text in the article. In the next sentence, referring to XFree86, what is referenced is a mention of removal of instances of Duff's Device from XFree86 shrinking it by a large amount. This is unrelated to pipelining and branch prediction, but instead of cache usage. 75.101.123.180 (talk) 10:33, 9 June 2011 (UTC)

PHP version
(this text is a "wiki formatted copy" from PHP Manual, For (loop), User Contributed Notes, bishop 17-Jul-2003)

This is a modified form of Duff's original device, because PHP doesn't understand the original's egregious syntax. That's algorithmically equivalent to the common form: $val++ can be whatever operation you need to perform ITERATIONS number of times.

Example of average run time across 100 samples with ITERATIONS = 10000000 (10 million) is: Duff version:    7.986 s  Obvious version: 27.608 s --Krauss (talk) 09:59, 26 July 2011 (UTC)


 * As far as I can read PHP the above is not equivalent to Duffs device, but just an example of loop unrolling. The defining characteristic of Duffs device is that it handles any remaining iterations in loop unrolling by interleaving a switch statement with the (do-while) loop. Lklundin (talk) 13:33, 26 July 2011 (UTC)

minor simplification
Since the whole point of the device seems to be source code optimization, it might be worthwhile to notice that the brackets in the switch statement are redundant. That is, the following is also valid C: — Emil J. 17:39, 4 November 2008 (UTC)
 * The ORIGINAL goal of Duff's device had nothing to do with gratuitously creating obscure source code syntax; it was a strategy to modify the source code to work around early C compilers' deficiencies in optimizing the execution speed of certain types of loops. Omitting the brackets around the switch statement would not be expected to have any impact whatsoever on the compiled output, so keeping the original goal in mind, there's no benefit in omitting them.
 * Granted, this entire exercise demonstrates some fairly controversial syntax which can easily obscure the actual operation being performed by the loop, so adding a little bit more syntactical mystery doesn't really hurt. But personally, I would defer to the common convention that switch blocks are always surrounded by brackets.
 * With modern processors, and modern C compilers' more advanced optimization capabilities, the more straightforward construct will often be just as effective (if not more so), so this entire technique is probably only relevant for historical purposes, or for very specialized, extremely speed-sensitive scenarios where it can be positively demonstrated that the more straightforward approach yields sub-optimal execution speeds for a specific combination of compiler and hardware. 24.222.2.222 (talk) 17:25, 5 August 2014 (UTC)

This is an interesting, but *bad* idea.
I wish there were a way to place universal caveats about coding style. You don't ever code for a competent seasoned engineer to read, you code for a mid-level engineer hopped up on caffeine at 3am to read. Cascading fall-throughs like this are just silliness and trouble waiting to happen.Tgm1024 (talk) 15:09, 20 November 2014 (UTC)


 * Hello! This, in general, isn't a forum; however, programming is pretty much like advertising –  everything is about the target audience.  Like there isn't much advertising around high-end server-class motherboards, for example, there's also code that simply isn't intended to be maintained later by caffeinated Joe Averages. :) &mdash; Dsimic (talk | contribs) 21:48, 21 November 2014 (UTC)


 * Bad metaphor: In an ad, you know roughly who the target audience is simply by the placement of the ad (the correct magazine, etc.) And if it's a general placement without target (a billboard for example), then the non-targets simply ignore the message.  Code is different: You cannot code for a non-caffeinated high-end engineer down the road.....you have no idea who will be handling things, and he has no ability to "ignore the billboard".  Besides, the most senior guys in the world will have a hard time with such things late at night (and after 12 diet cokes) just trying to meet a crazy deadline.  Defensive programming is a critical concept.  The idea of "just get better engineers" has always been broken logic.
 * Now I don't buy into this concept in all cases. For instance, the witch-hunt against the goto still in 2015 seems to have legs when it should not, and there are still people advocating "if (0==variable)" in languages like C where assignments are also expressions, and banning multiple returns from functions/methods, all of which are good examples of over-kill "nanny coding".  But when it comes to absurd idioms like this crazy crap:


 * ....then you have to start putting down the ego and start thinking about clarification instead. Yes, we all know what it does.  No, that doesn't mean it's a good idea.
 * Tgm1024 (talk) 20:37, 25 January 2015 (UTC)


 * Well, it's perfectly fine if we disagree on something. However, I still don't think that dumbing everything down is the way to go in all environments; if we'd take that route, why should anyone use C in the first place?  Programming in something backed by a virtual machine is always much better for deadline-pressured caffeinated juniors, seniors, or whoever you refer to.  Also, I've seen a lot of awfully bad C and C++ code written by those "take it easy" developers, which was so bad that equivalent but well-written PHP code ran faster with all the overheads of interpreted execution.  Also, I've seen a whole lot of awful PHP code.  In a few words, there's no substitute for real knowledge –  neither the choice of language nor complex or simple constructs help if one doesn't know what he or she is doing. &mdash; Dsimic (talk | contribs) 23:29, 26 January 2015 (UTC)


 * Please, this talk-page is reserved for discussions on how to improve the article. It is not a forum for discussion of programming. Please continue only with specific suggestions on how to improve the article. Lklundin (talk) 07:46, 27 January 2015 (UTC)


 * Well, that's exactly what I've said back in November 2014: "this, in general, isn't a forum". &mdash; Dsimic (talk | contribs) 07:55, 27 January 2015 (UTC)

Potential reference
Lesser Cartographies (talk) 02:35, 18 September 2015 (UTC)


 * Hello! That's a great find, thank you;  into the article as a reference. &mdash; Dsimic (talk &#124; contribs) 08:33, 18 September 2015 (UTC)

This article is about a specific piece of code
This article is about a specific piece of code, written by Tom Duff. Much like the Egyptian Pyramids, the purpose of this article is not for editors to modify the original design with what they consider improvements to this design. And please list only alternative implementations via a WP:RS. The same goes for comments on the usefulness of the code. Therefore, please revert your edit. Thank you. Lklundin (talk) 18:08, 10 October 2015 (UTC)


 * 1) Removing or adding white space isn't changing the code, and nothing you listed is a valid reason to revert those white space changes. •  Sbmeirow  •  Talk  • 21:22, 10 October 2015 (UTC)
 * 2) The code in "Simplified explanation" section is NOT the original Duff's code that was originally posted on the USENET or BBS. I just now opened up the Dr Dobbs article and looked at it, and wow, guess what, my code changes (>>3 and &7) that you reverted were actually in this article, so it's actually back on you for being overly aggressive on your reverts.
 * 3) Simple code optimization are similar to English grammar changes, and a person doesn't have to provide a cite obvious mathematical statement like "1 + 1 = 2". See You don't need to cite that the sky is blue and Common knowledge.
 * • Sbmeirow  •  Talk  • 21:22, 10 October 2015 (UTC)


 * Hello! While the new "Improvements" section introduced in  surely doesn't belong to the article due to its original research nature (clearly, no references were provided), whitespace and code formatting changes restored in  are rather fine because the  section describes a functionally equivalent version of Duff's device. &mdash; Dsimic (talk &#124; contribs) 22:52, 10 October 2015 (UTC)


 * Hello Dsimic, Thanks for chiming in and explaining the inappropriately and misleadingly summarized edit by Sbmeirow. Lklundin (talk) 06:01, 11 October 2015 (UTC)


 * You're welcome, and I'd just add that it's all about discussing any issues that may arise along the way. &mdash; Dsimic (talk &#124; contribs) 06:07, 11 October 2015 (UTC)


 * A) My edits clearly don't deserves the abusive term "misleadingly" as stated by Lklundin.
 * B) Compare my code example to code that is half way down this reference from the article, a section called "A Reusable Duff Device", then it's very obvious my code example is similar to the Dr Dobbs article.
 * C) Concerning my original edit "The original code was written in an era where variable declaration was lax. Newer compilers will complain about the lack of a type for count and n.", which is common knowledge for engineers/programmers so this doesn't fall under "original research" either.  It's amazing this article does NOT state these code examples are ancient K&R C coding style, instead of modern ANSI C/C89/C90/C99 coding style.  This is basically the point of my edit, which I should have stated differently, yet still neither is "original research".
 * D) This article needs an ANSI C or preferred C99 memory copy example near the bottom, because this is 2015 not 1983. Actually the code in "Simplified explanation" section should be "ANSI C" compliant at a minimum, instead of the out-dated crappy K&R style that new compilers don't support by default.
 * E) We need more coding examples in all Wikipedia coding articles, even if they fall under the blatantly abused description of "original research". Coding newbies have migrated to StackOverflow and StackExchange, thus we need to come up with a better way to improve coding articles, and quit using "OR" as a lame excuse for not doing it.
 * • Sbmeirow  •  Talk  • 10:13, 11 October 2015 (UTC)


 * Look, it's great that you are such an exceedingly knowledgeable programmer. The thing is just that per WP:NOTTEXTBOOK it is not needed here. Oh, and try to understand the difference between an adjective and an adverb (as in e.g. "inappropriately and misleadingly summarized", as opposed to the completely different use of adjectives to describe a noun such as "edits"). All the best, Lklundin (talk) 11:04, 11 October 2015 (UTC)


 * Please, let's keep in mind that Duff's device presents pretty much an important programming artefact. As such, having "crappy", outdated K&R coding style actually helps in reflecting the historical nature, IMHO.  However, mentioning in the article that the used style is K&R would be only beneficial. &mdash; Dsimic (talk &#124; contribs) 14:38, 11 October 2015 (UTC)

Stroustrup's version
I don't believe that it is Stroustrup's version at all. In 'The C Programming Language' by Kernighan and Ritchie they say that the most important conceptual code in the c programming language is "while(*dst++=*src++);". I havent read that book in more than a decade, but that code is in there.

Without seeing what is actually written in Stroustrups derivative of the c programming language (book and language), I can not say for sure that I gotten this context wrong. Regardless, the syntax as shown in the example is c-code NOT c++.

Therefor I nominate the section for delete, as it is wrong. — Preceding unsigned comment added by 86.12.140.205 (talk) 15:40, 10 March 2016 (UTC)


 * Well, the content in the section is backed by a reference, which I just checked to be correctly cited –  it matches exactly what's on page 141 in Bjarne Stroustrup's book The C++ Programming Language.  Thus, the section is perfectly fine. &mdash; Dsimic (talk &#124; contribs) 19:26, 7 April 2016 (UTC)

External links modified
Hello fellow Wikipedians,

I have just modified 2 one external links on Duff's device. Please take a moment to review my edit. If you have any questions, or need the bot to ignore the links, or the page altogether, please visit this simple FaQ for additional information. I made the following changes:
 * Added tag to http://www.l33tskillz.org/usenix2003/notes/t-09-5/
 * Added archive https://web.archive.org/web/20051013062233/http://www.sics.se:80/~adam/pt/ to http://www.sics.se/~adam/pt/
 * Added archive https://web.archive.org/web/20080828170858/http://www.eigenclass.org/hiki/threadring-with-protothreads to http://eigenclass.org/hiki/threadring-with-protothreads

When you have finished reviewing my changes, please set the checked parameter below to true or failed to let others know (documentation at ).

Cheers.—InternetArchiveBot  (Report bug) 12:50, 17 December 2016 (UTC)