Protothreads and Other Wicked C Tricks
lwb writes "For those of you interested in interesting hard-core C programming
tricks: Adam Dunkels' protothreads library
implements an unusually lightweight type of threads. Protothreads are
not real threads, but rather something in between an event-driven
state machine and regular threads. But they are implemented in 100%
portable ANSI C and with an interesting but quite unintuitive use of the switch/case
construct. The same trick has previously been used by Simon Tatham to implement
coroutines
in C. The trick was originally invented by Tom Duff and dubbed Duff's
device. You either love it or you hate it!"
I used a Lifeboat lib back in the late 80's that this reminds me of. Cooperative multitasking. Eventually ported the whole thing to OS/2 and used that threading instead. All the code pretyy much worked as-is.
The revolution will NOT be televised.
So this is so "counterintuitive" that no one else will ever understand your code?
Sounds ideal!
Duff on Duff's Device:
http://www.lysator.liu.se/c/duffs-device.html
--- These are not words: wierd, genious, rediculous
I first came across this while I was working on the e-voting machines. There was a dept especially allocated to investigating how to hide certain features in c code to make them look like soemthing else.
I don't program in C that much any more, but it would be nice if this was implemented in higher level languages. I don't know if they would gain anything, but it's at least interesting.
Send email from the afterlife! Write your e-will at Dead Man's Switch.
Duff's device is a way of forcing C to do a form of loop unrolling. It has nothing to do with coroutines.
This looks very similar to the implementation technique used for the Squeak programming language (not the Smalltalk Squeak). Squeak is a preprocessor for C that makes it very easy to use this technique.
http://citeseer.ist.psu.edu/cardelli85squeak.html
Doug Moen
I have written a truly remarkable program which this sig is too small to contain.
While it is probably the first time the copy technique had been expressed in C, it's certainly not the first time the actual technique had been expressed in code.
I recall seeing the same trick implemented in assembler somewhat earlier, I think the technique was called towers?
1) Your operating system in written in Java.
2) Your email client in written in Java.
3) Your browser is written in Java.
4) Your music player is written in Java.
5) Perl in written in Java.
6) Your word processer is written in Java.
So there !!! Java rules !!!
not a problem in c# though :)
just put a nice "unsafe" modifier and start using pointers... on linux via mono... oh yeah
SGI had state threads library since long http://oss.sgi.com/state-threads
...not bound to any particular OS.
0 .x/ggAddTask.3.html
If that's what folks are looking for, another option is the tasks added to LibGG a while back. Tradeoffs either way -- LibGG's requires at least C signals (but will use pthreads or windows threads if detected during compile time), whereas this can be used in OS-less firmware. But on the positive side you can use switch() in LibGG tasks -- what
you can't use are a lot of non-MT-safe system calls. It's an OK abstraction but of course there are so very many ways to accidentally ruin portability that it is far from foolproof.
http://www.ggi-project.org/documentation/libgg/1.
Someone had to do it.
Since there's really no place to discuss this type of thing "on topic", I'll suggest it here.
Can we please have a bayesian filter that automatically starts these things out at say -2 (only possible if you upset the filter)? There are often funny posts that get "bad moderation" and wind up modded -1, so I like to read at -1. However, that unfortunately means I have to see all the GNAA trolls, etc.
The PPC architecture has a special-purpose count register with specialized branch instructions relating to it; e.g., the assembly mnemonic 'bdnz' means "decrement the count register by one, and branch if it has not reached zero." I've used this in some pretty weird loops, including this one that broke the Codewarrior 9.3 compiler (fixed in 9.4.) This computes the location of the n'th trailing one in a 32-bit integer. Pardon my weak attempt at formatting this in HTML:
static uint32 nth_trailing_one(register uint32 p, register uint32 n) { end: }
return __cntlzw(p ^ (p - 1));
}
The idea was that the instruction stream should stay as linear as possible; most of the time the branches are not taken, and execution falls through to the next line of code. Ironically (siliconically?), the entire function could probably be implemented in a single cycle in silicon; shoehorning bitwise functions like this into standard instructions tends to be extremely wasteful. Perhaps FPGA's will make an end run around this at some point. I've also tried this function with a dynamically-calculated jump at the beginning, similar to the case statement logic in the article.
Hmm, I had a point I was trying to make with this post, but now it's escaped my mind...
Weeks of coding saves hours of planning.
> You either love it or you hate it!
...
Or you have no clue what this post is about
Hideous, but efficiency is not it's problem.
I got to this little gem:
My English parser thread shut down at that point . . .
Seriously, this looks like a handy little thing for low-memory systems, though I'd be a bit hesitant about pushing at the C standard like that--the last thing you need is a little compiler bug eating your program because the compiler writers never thought you'd do crazy things to switch blocks like that.
Would this technology be more suited to limited embedded systems? From a casual glance it would seem perfect for systems with specs measured in kilobytes not gigabytes. Especially where operations as close to realtime as possible are highly valued.
Is this similar to Stackless Python and green threads? I spend most of my time with interpreted languages, so my C is very lacking.
random underscore blankspace at ya know hoo dot comedy.
If it's anything like microthreads, then it's not inefficient at all. I have played around with microthreads in python and easily achieved over a million 'concurrently-cooperative' threads.
Disclaimer: I haven't read the article yet, so maybe it has nothing to do with microthreads.
Um, who cares? Really? Shouldn't this be hidden under "Developers"? I mean, I'm a developer, and this might be great to someone who might use it... but that ain't me. (Self-centered huh?)
NO TOUCH MONKEY!
Weightless threads in Python:
y thrd.html
http://www-128.ibm.com/developerworks/library/l-p
They are cooperative but far more efficient than Python's own threading model. You can easily create hundreds of thousands of concurrent threads.
This is bad, lame, faux cooperative threads.
It's also not even particlarly new [1998].Unless memory is at an absolute premium, just use cooperative threading instead. If you try to use prototheads, you'll quickly discover how unlike "real" programming it is. Even just a 4K stack in your cooperative threads will get you way more than protothreads does.
I already crashed Linux with my code, let alone implement something like this.
"I'm a well-wisher, in that I don't wish you any specific harm."
Don't need a low overhead pseudo-thread implementation for what I do, but would be nice is
a stack-safe and stable(same thing) setjmp() for process oriented posix compliant stuff.
Anyone got an implementation?
From the above Duff on Duff's Device: I have another revolting way to use switches to implement interrupt driven state machines but it's too horrid to go into.
Perhaps this is the Duff's Device equivalent of a proof of Fermat's Last Theorem? Or is my ignorance of the history of Evil Computing showing?
//Information does not want to be free; it wants to breed.
Ummm, which operating system would that be? Not all programmers have the advantage of an operating system as such; my current development target has no OS, runs at 8MHz, and has 4kbytes of memory. Something like this could be extremely useful for me.
Wow. And I used to think C was frightening when I discovered the fun you can have with a program that takes command-line arguments when you start making recursive calls to main().
And just what's wrong with that?
David Gould
main(i){putchar(340056100>>(i-1)*5&31|!!(i<6)<< 6)&&main(++i);}
Actually, I don't know if this is exactly the same feature, but it sounds like it can be used that way. Check out the What's new in Python entry. This is currently implemented in Python CVS, to be available in Python 2.5.
The actual Python Enhancement Proposal gives more detail and several badass use-cases.
It's rare that you're presented with a knob whose only two positions are Make History and Flee Your Glorious Destiny.
Get the book Obfiscated C and Other Mysteries by Don Libes. Explanations of various Obfuscated C contest entries, and alternate chapters illustrate neat corners of C, including a few things similar to this little library. Occupies a place of honor on my shelf.
PHEM - party like it's 1997-2003!
"The competent programmer is fully aware of the limited size of his own skull. He therefore approaches his task with full humility, and avoids clever tricks like the plague." -Edsgar Dijkstra
Fetch Text URL - Firefox Extension
Thanks
Even if you are writing in the purest of C, you aren't guaranteed that the optimizer isn't going to very reasonably want to introduce the equivalent of local variables. And even if you are sure there's no optimization going on, you STILL don't know for sure that the compiler isn't using space on the stack. There just is no guarantee built into the language about this. And if you were wrong, you'd get strange, highly intermittent and non-local bugs.
You could be pretty sure. You could force the compiler to use registers as much as possible. You could keep your routines really short. (Hey, if they don't preserve local variables, then how do they do parameter passing?? Parameters are passed on that same stack!)
But to be completely sure, you'd have to look at the output code. It wouldn't be too hard I suppose to write a tool to automatically do it...you'd just look for stack-relative operations and flag them. But then what would you do if something wasn't working? Yell at the compiler? Rewrite the machine language?
I guess I don't quite see the use now I've written this up. When is memory THAT important these days? It ain't like I haven't done this, I've written significant programs that I got paid money to do that fit into 4K (an error correction routine).
But that was an awfully long time ago. Now it's hard to find memory chips below 1Mbit. That two byte number is interesting but your "threads" aren't doing any work for you -- the whole point of threads is that you are preserving some context so that you can go back to them.
And since you can't use local variables, you can't use things like the C libraries or pretty well any library ever written, which is teh sux0r.
For just a few more bytes of memory and a few more cycles, you could save those local variables somewhere and restore 'em later. Suddenly your coding future is a brighter place. Tell the hardware people to give you 128K of RAM, damn the expense!
You could even put in a flag to indicate that that particular routine didn't need its local variables saved so you'd get the best of both worlds, use of external libraries as well as ultra-light switching.
It's ugly as sin, but your compiler had better get it right, or else it's not a C compiler.
Normally if you want multithreading and have no OS to do it for you, you'd use an interrupt handler to swap all registers, as well as the stack location. Even a 400 byte stack is adequate if you have no recursion or local arrays. So really, this is only for situations where you don't even have 400 bytes to spare per thread. The complete inability to use local variables makes this useless except in very specific situations.
I could see this being used if you need a very large number of threads with no local variables. I'd still be very wary about using something that does not preserve the content of registers when switching 'threads'.
As the prothread homepage says, it's for extremely small embedded systems, where there are no operating systems, with tiny amount of memory (You can't use DRAMs on systems that cost something less than $1). Want to use threads on those kind of systems, you have no choice.
Another advantage is its portability. Small embedded systems, whether they have operating systems or not, usually can't support some fully-blown threading standard. Those operating systems seem to implement some kind of 'specially tuned' thread APIs.
Using these kind of threads on a full-blown PC (or servers) would have almost no benefit. However, in the embedded software engineer's perspective, it's great to see a ultra-lightweight thread library without any platform-dependent code.
Do you even lift?
These aren't the 'roids you're looking for.
As far as ridiculously low memory constraints go...
I was talking to a friend the other day, who had to write the code for a car door opener dealie. You know the one. A really nice, high end one with an LCD that displayed stuff (not your average 100% hardware door opener). His code had a staggering 256 bytes of RAM to work with, and even then they were potentially 7 bytes overbooked. So yes, these kinds of constraints still exist. Sadly.
The summary only claims that the 'interesting but quite unintuitive use of the switch/case construct' is originally Duff's device, not application to coroutines.
you had me at #!
You either love it or you hate it!
Or you don't care, having long since given up on low-level languages so you can actually be *productive*...
You Slashdotted PuTTY! You bastards!
It's too clever to be really useful unfortunately. The big issue is of course the no "local variables". Trouble is, if you are writing in C, the compiler may well be creating local variables for you behind your back. In C++ for example there are many cases where this will certainly happen, like
void DoSomething(const string&);
DoSomething("hollow, whirled");
where a local variable of type string will be temporarily created to pass to routine DoSomething.
You need to read the article.
It only says you can't use local variables across functions that block. Actually, it doesn't even say that you can't use them, it only says don't expect their value to be preserved.
In your example, even if the compiler does create a local variable to call DoSomething, and even if DoSomething does block, who cares if the value of that local variable is preserved, since it's impossible to reference it again after that statement?
But that was an awfully long time ago. Now it's hard to find memory chips below 1Mbit.
I can help you with this problem! Is 16 bytes small enough?
And since you can't use local variables, you can't use things like the C libraries or pretty well any library ever written, which is teh sux0r.
But you can use the C libraries. Just don't use local variables across functions that block. Only a very few C library functions block.
I downloaded it. But the version that is there does not, in fact, compile: there's an obvious typo at example-buffer.c, line 137:That aside, I believe your claim is wrong now that I've read the code.right? The reason is that you are simply case-statementing into the code and bypassing the whole subroutine calling mechanism entirely -- so the variable initialization just isn't called.
Now consider the following code:Suppose the compiler decided to extract out the common y*(2*pi)/360 term as an optimization. The assignment to that common term will not occur when you jump into the middle of that routine -- so your code will not work as it appears.
sweet jesus. i remeber learning recursion in college (not too long ago). now i find out main() can be recursive? will the madness never end?? God i need to code again... too much tech support...
I read it as pothead, post a smily if you did to.
Just as a question: Why do people thing threading is extra hard to do? without much exp so far it seems to help in that the code snippets are smaller and theirfor less prone to typos (then again only ones I've used to this point are in java with a IDE)
But the compiler won't decide to do this because it won't be able to establish that y (or pi) can not be changed between instances of this code.
In your second example, the compiler *cannot* remove the sub-expression because the case statement that gets you there crosses a basic block boundary; the return statement from the blocking code, and the jump in through the switch are caught as valid C constructs that prevent the common sub-expression optimization.
The macros as given give you a semblance of variable continuity and scoping, but the compiler, after the macro substitutions, just sees a return on the end of the blocking section, and a switch into the code following, something like:
The ugliness in the macros is about letting other control constructs live around your switch in, but still generates legit code, but, for example, you can't use a local varible as an index to your for loop. Yuck.Debugging is always harder than coding, so any person that makes code as clever as he possibly can is by definition not fit to debug it. (not my quote and cant find reference hence AC)
Brilliant programmers know this, its the Egotistical ones that think they have to be clever, the job is never done at coding.
Is thre some similarity between these protothreads and fibers?
Have fun: Join D.N.A. (National Dyslexics Association)
Okay, I'll play the n00b. I understand most of this, but my coding background is not that great, and mostly in C++, Java, and PHP, and I'm having problems with the switch from Duff's Device...
switch (count % 8)
{
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
What the hell is up with that do { applying only in case zero? It's in several places on the net just like that and Visual Studio compiles this just fine, so it's not an error. I checked K&R, and they don't even hint at what could be going on there... I'm lost. Help?
Telltale Games: Bone, Sam and Max
indeed, this code actually rocks from one point of view, the non-context-switching point of view :)
...)
people are all so excited currently about kernel thread and pthreads and stuff like that, but what they dont realize is that it's terrible overhead actually makes your app slower than it would be if it was built on a single process model. this "threading" api is a quite wicked implementation, but it works in simple cases and since it doesn't switch the registers back and forth all the time like your 250 pthreaded app, it's fast. on the other hand, it's fairly incompatible with signals and stuff (i think) and i have no idea what this poor library does when it meets regular blocking i/o code. most probably it will just block.
anyway, the desktop machines and servers today dont really care that much about which threading api they use, but in stuff like cellphones, palm handhelds and other quite slow operating devices that dont have a common threading interface, this is a quite good solution.
it's a good thing alright, just try to think out of the box and look where it can be used (i will go and multithread my microwave now
I'd tell you the chances of this story being a dupe, but you wouldn't like it.
FYI this technique is heavily exploited in the programming language Felix:
.. just a tad faster than Linux. Both MLton and Haskell also support this style of threading with high thread counts and switch rates (although the underlying technology is different).
http://felix.sf.net/
to provide user space threading. The main difference is that all the 'C tricks' are generated automatically by the language translator. If you're using gcc then the switch is replaced by a computed jump (a gcc language extension). On my AMD64/2800 time for creating 500,000 threads and sending each a message is 2 seconds, most of the time probably being consumed by calls to malloc, so the real thread creation and context switch rate is probably greater than Meg/sec order
John Skaller mailto:skaller@users.sf.net
I'm lost too. What is this supposed to do? Copy from "from" to "to"? Then why is the "to" pointer never being incremented? Is this another Microsoft interview question? Did I get the job?
http://groups.google.com/group/net.lang.c/browse_t hread/thread/dca16afbaec3cdd4/66008138e07aa94c?lnk =st&q=%22tom+Duff%22&rnum=2&hl=en#66008138e07aa94c
I think that's it. note the reference to the mention of the interrupt driven state machine that's too horrible to describe
my current development target has no OS, runs at 8MHz, and has 4kbytes of memory
Sounds like damn luxury to me. My current project has 2kbytes ram, 4.9MHz clock. And they need it to do math(add, multiply, divide) on 30 odd 64-bit variables.
Young people these days just don't know how good they've got it.
GCS/S d-x s+(+): a C++++$ UL+$ P+ L++$ !E--- W++@ N++>$ !o !K-- w++$ !O !M !V PS++>$ PE !Y PGP+ t+ 5++ X++ R tv b
But it became largely unnecessary when optimizing compilers became available when RISC came about. A good compiler will unroll a simple loop like this smarter than you can.
Besides, on any modern machine you'll get more speed up by copying a word at a time instead of tightening your byte-at-a-time loop. Because due to the caches, you'll likely end up moving cache lines at a time over the bus anyway (assuming stuff is aligned), and so the key becomes to minimize the number of instructions needed to do it. And going to 32-bit copies cuts that down to 1/4.
All this stuff might be unintuitive to some, but frankly, case statements are just gotos with optional exits (breaks), so once you know that, this doesn't seem all that odd.
http://lkml.org/lkml/2005/8/20/95
Ever heard of Lambda Calculus?
I guess today is a passable day to die.
Funnily enough, that was exactly what I thought it said, and I hadta go skin up a d00b. Aren't hands really interesting things? I'm hungry. Oh wow, these noodles taste divine! By the time I got back to my workstation, someone else had already posted it.
Any modern high-level should have ... oh wait, never mind.
Can people please stop show some consideration and stop slashdotting Chiark please? I want to read my e-mail.
Thank you.
anyway, the desktop machines and servers today dont really care that much about which threading api they use, but in stuff like cellphones, palm handhelds and other quite slow operating devices that dont have a common threading interface, this is a quite good solution.
.NET) are properitary and subject to market power struggles. So we are sadly probably stuck with C++ hell for years to come yet.
While your argument concerning cellphones certainly has been valid historically, todays devices are actually very capable. I work with cellphone software and I found myself beeing suprised e.g by the performance of J2ME on modern phones. My experience is with symbian (c/c++) and J2ME. Symbian OS encourages use of cooperative multitasking though its "active objects"-pattern to eliminate some of the overhead of real threads. However using J2ME yields almost the same performance without having to deal with all the "performance optimizing" tricks of Symbian C++ (such as active objects, descriptors instead of strings and manual memory (de)allocation). (It is possible that this is due to java-optimized hardware in the phones, we have tested new Sony Ericssons and high-end Nokias, but who cares it works very well)
When high level languages/platforms like Java can successfully be used even on cellphones, its is time for C++ to die as language for application developement. The only obstacle, as I see it, is that the two promising platforms (Java and
Scitne aliquis remedium potimum crapulae?
> The standard forbids calling the entrypoint. Calling main() is undefined.
In your head it's undefined. In the C standard there's no such limitation.
> Besides, it's silly.
Probably.
If you follow the link to Simon Tatham's coroutines, there's a link to Duff confirming that he was referring to the same thing.o mments
http://brainwagon.org/archives/2005/03/05/1060/#c
rant
"The device is indeed perfectly valid, legal C, and many compilers will optimize the switch into a jump table just as would be done in an assembler implementation."
This is another example of trying to make and optimizer out of a compiler. "many compilers" does not mean "all compilers" which in turn is going to bite you in the ass at some point.
Join the Slashcott! Feb 10 thru Feb 17!
Unless you try to 'yield' something from within your own 'switch' statements. Then such 'smart' macros will silently pollute current 'switch' block with bogus case values, so it:
1) silently modifies you 'switch' statement sematics
2) fails to continue from the right spot on next iteration.
You seem to be working under the impression that your target machine only has 1 CPU (and that CPU only has one core). Using 2 CPUs with a 20% overhead from thread locking etc. is a lot faster than using one without the overhead. Most new CPUs have two cores. In 18 months, they will have 4, then 8 etc. They will also have SMT support. If you want to future-proof your code (and it's at all performance-dependent), then make sure it exhibits a high degree of parallelism.
Of course, if you want your code to be at all maintainable, then avoid threads like the plague, and pick a language like Erlang that has asynchronous message passing primitives.
I am TheRaven on Soylent News
Anyway, it sounds like fun - although possibly less so if you have some kind of deadline.
I am TheRaven on Soylent News
well, this IS the OS. A simple embedded OS typically consists of these parts:
...
- mem manager (malloc etc)
- threads
- some sort of locking mechanism
Nice to have
- some sort of queues, message systems
Luxurious:
- file system for flash or ram
everything else is already "luxury", such as printf facility, network stack,
So - those simple threads are the OS. A very small one with lots of restrictions. But it is so nice to have such functionality instead of having nothing.
i agree that having 2 threads on a 2way machine is quite a good idea ... and that having 16 threads on a 4way 4core processor machine is sometimes a good idea. but people are threading everything today ... even stuff that doesnt need to be threaded :'(.
:)
if you count all the threads that run in an usual linux/apache/mysql box that in addition has a threaded mailserver attached to it (and a some lightweight java applications runnnin on tomcat in java threads), then the poor machine is just overkilled. even if it's a 4-way box. if your mysql spawns itself into 50 threads to handle 50 clients, than on a machine that can handle 16 processes at the same time (thanks to its 4x4 cores) is still in big trouble, and the locking/sharing& registry pulling/pushing overhead is indeed a plague.
parallel computing is a ofcourse the way of the future, but people should try to remember that there are other ways than threads to do parallell stuff, and if you leave aside the hyper/mega/giga hype about common threading tricks, then sometimes forking can just perform better, and in some cases, you have to go pvm or mpi right from the start to avoid massive code rewriting later. (usual threads over an 100node beowulf is fun but... not as effective as you'd think, btw. is there anything besides openssi that can pull that off ?)
when i first saw threads in year 2000 in java, i was amazed, they felt so good and easy to handle, it was rather hard to make a mistake there, and it all worked fine. i never thought about performance. during the last year i have fought with the performance issues a lot. and that "yberthreading" disease seems to spread even more with every day that passes.
ofcourse we can't say that threads are "all bad", actually they are really good in some spots, and in certain conditions they help you to keep a clean sane code and are the best solution. but people please, just dont overdo it
I'd tell you the chances of this story being a dupe, but you wouldn't like it.
I am working to optimize some medical device analog IO code that runs on a PIC processor with 191 bytes of memory. This may help. Real ideas like these are a lot more fun than the flamebait political stuff that has predominated on this forum for the past few years.
an ill wind that blows no good
Because when you have a 20 MHz CPU that has to do 500,000 loops per second, it *matters* whether those loops take 35 cycles or 45 cycles.
The 2 things that separate the assholes from the stars are: Knowing when it is worth spending hours to optimize, and Documenting what they did and why.
"Surplus that Corba and refresh them with some Duff's devices. I think we ought to get the multi-core ones to cut down on licensing costs."
When the people fear their government, there is tyranny; when the government fears the people, there is liberty.
Most people think that we have almost infinite processing power and memory everywhere. The truth is that most application do not need that much. Like the car door openner mentionned in post above, there are microchips on many small gizmos. As soon as it needs to be small, low power and low cost there is a pretty good chance it uses a 8051 microcontroller in it. That means 12mhz with 256 bytes internal ram. Actually the ram contain special registers, the stack, bit addressable section, so you don't really have all that 256 bytes for anything you would want. 8051 are ideal for those small applications and as strange as it might seems now, you can still do a lot with that amount of ram if you don't play stupid.
efficiency is not it's problem
"its".
The largest Atmel AVR microcontrollers have 8k of SRAM. PIC microcontrollers are, as far as I know them, even more limited. A more typical RAM size for them is around 128Bytes (not kB!). There is just no way to get 128k of RAM easily with those devices. That's what is meant by "severely memory restricted".
Something doesn't look quite right in the first example http://www.sics.se/~adam/pt/examples.html>
Consider the sender -
1) an acknowledgement is successfully received (the timer has not expired)
2) the timer expires
3) the timer is checked again in the condition of the while loop
4) the packet is resent even though it has been correctly acknowledged
In C, you can use setjmp/longjmp for simple cooperative threads. You do not even need to setup a stack (but then you get no local variables).
:)
Instead of 2 bytes, you need enough bytes to hold the machine state (sizeof(jmp_buf)). It's a little more overhead but a hell of a lot easier to follow than some macro'd switch statements.
Is it just me, or this looks like a low level implementation of what you can easily do with the C# "yeld" statement?
Actually, wouldn't you use "yeld" in C#?
> Probably.
That's the point, isn't it? ;-)
I'd rather be flying
Ia! Ia! __cntlzw... oh wait - *ahem* sorry..
Your hybrid is not saving the environment. Its purpose is to make you feel good about buying something.
Completely proper standard intarweb 3n9/15h 2.2
the preceding comment is my own and in no way reflects the opinion of the Joint Chiefs of Staff
I know what you mean. I've also used PIC's a lot (although I prefer the ubicom/SX chips for their speed). This is a great technique for handling concurrent processes in a menatally manageable way.
This is very similar to the technique used to multitask in the interrupts. To time-share the interrupts it's common to use a similar jump-table structure.(so that you can have serial comm and pwm and the like all happening at once with proper predictable timing)
Then again, might as well go with a more reasonable and pleasant microcontroller, like the MSP430.
(I also program uC's for medical device related things. I'm just finishing up my Ph.D. in analytical chemistry... working on some photon time-of-flight spectrometers for in-vivo measurements.)
I agree as to the usefulness of this story. I think there should be more like this.
The general rule of threading is this:
If an operation can block, and prevent system responsiveness, then a thread is a viable means of working around the issue.
With that in mind, worker pools of 50 threads in apache make sense, since each thread can iterate [select()] amongst 1-n sockets, and the case of 1 socket blocking abnormally will not render the entire webserver immobile.
In a GUI application, when a user executes a long-running task, like "generate a report", you'd trigger it in a thread so that your message pump can get back to work keeping the window drawing primitives going.
Other uses of threads, such as compute tasks, are less wise, IMHO, but sometimes they have their places.
Which is exactly why I ended up using protothreads for a PIC microcontroller based system that needed some concurrency but didn't have the resources to support a more full-featured OS. With protothreads, I was able to input serial data from a couple of devices, drive an LCD display, take user input, etc. For all but the simplest implementations of microcontroller-based systems, protothreads are a terrific help.
From wikipedia on the page linked from the story:
This article has recently been linked to from Slashdot. Please watch out for any trolls that may target this article
Well, well, this crowd has a good reputation!
Ah Wikipedia...some prick thought he was funny and added little ironic comment to the Duff's Device page: This article has recently been linked to from Slashdot. Please watch out for any trolls that may target this article. /. suks!!!
From IP address: 139.168.1.230
Enjoy.
-tom
I'd click on that.
I have addition & subtraction, (integer) multiplication and division, but most of the "hard work" is done with an FPGA. The application doesn't have much need for arithmetic anyway, so there's no issue with speed. It works well, and yes, it is fun.