4 ms·
As a pure C & C++ programmer, I was amazed of the (to my eyes) incredibly clever little trick that is the Duff's Device [0], and started using it right away to
by j1elo 7y ago
As a pure C & C++ programmer, I was amazed of the (to my eyes) incredibly clever little trick that is the Duff's Device [0], and started using it right away to provide embedded programs with easy to write "concurrency" [1], [2].
Some time later I had to dip my toes into the world of Javascript, learning first about Promises, and finally reading about async/await. I just realized that it is basically the same trick I had been using all along. And now it's coming to Rust, neat!
[0]: https://web.archive.org/web/20190217162607/http://www.drdobbs.com/a-reusable-duff-device/184406208 https://web.archive.org/web/20190217162607/http://www.drdobb...
[1]: http://dunkels.com/adam/pt/ http://dunkels.com/adam/pt/
[2]: http://dunkels.com/adam/pt/expansion.html http://dunkels.com/adam/pt/expansion.html
- Tyr42 7y agoI don't see how Duff's Device provides "concurrency"? It's a way to unrolls loops. Now surplanted by SIMD instructions. Or do you mean state machines?
- jcranmer 7y agoI assume that what the OP meant by Duff's Device is "switch in C is syntactically a goto" rather than the specific loop-unrolling paradigm of Duff's Device itself. An example usage I've come across is the bzip2 decompressor: https://github.com/enthought/bzip2-1.0.6/blob/288acf97a15d558f96c24c89f578b724d6e06b0c/decompress.c#L195 https://github.com/enthought/bzip2-1.0.6/blob/288acf97a15d55... (note that the case statements are hidden in all of the GET_* macro calls).
- iherbig 7y agoLook upon the horrors and weep: https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
- j1elo 7y agoActually I meant "coroutines". You create a main infinite loop in your application, in which you call all functions that are meant to perform activities that should be happening "at the same time", like for example taking ADC measurements, while updating a PID control algorithm, while waiting for the GPRS chip to connect to the internet; those would be their own function, called "protothread" if you use the Adam Dunkel's library, and they are going to run a number of blocking operations, waiting for some external hardware to do their thing. Each one of those functions is converted into a Duff device by means of the macros provided by the Protothreads library. Now each protothread function can express its own operations in a linear and very clear way, and yield execution to the other functions by just returning from the Duff device. When the main loop starts over, each of those functions will "resume" their operation at the point where they yielded during the last iteration. The way I see it, this trick is much more than just loop unrolling; it provides a C developer with the same basic features that an async function would have in JS; and the yield points in the functions would be equivalent to the 'await' calls. This allows for very easy to read code, being able to follow the logic of each individual "thread" without much of the noise needed for taking care of the "coroutines" part. The next step (in the embedded world, at least) would be going the route of an RTOS, such as FreeRTOS, but I've managed well so far without it. EDIT: and yes, this is basically also a neat was of streamlining a state machine
- that_jojo 7y agoMaybe I'm really dense, but what on earth does loop unrolling have to do with concurrency or asynchronous code?
- bryanlarsen 7y agoCooperative multitasking is typically done with a high level loop pumping a state machine. Duff's device is often quite useful in that implementation.