Date: | January 4, 2005 / year-entry #2 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20050104-00/?p=36813 |
Comments: | 11 |
Summary: | Another type of higher-order enumeration is composition, where one enumerator actually combines the results of multiple enumerators. (Everybody knows about derivation, but composition is another powerful concept in object-oriented programming. We've seen it before when building context menus.) In a producer-driven enumerator, you would implement composition by calling the two enumeration functions one after the... |
Another type of higher-order enumeration is composition, where one enumerator actually combines the results of multiple enumerators. (Everybody knows about derivation, but composition is another powerful concept in object-oriented programming. We've seen it before when building context menus.) In a producer-driven enumerator, you would implement composition by calling the two enumeration functions one after the other. In a consumer-driven enumerator, you would implement composition by wrapping the two enumerators inside a large enumerator which then chooses between the two based on which enumerator was currently active. A fiber-based enumerator behaves more like a consumer-driven enumerator, again, with easier state management. Let's write a composite enumerator that enumerates everything in the root of your C: drive (no subdirectories), plus everything in the current directory (including subdirectories). class CompositeEnumerator : public FiberEnumerator { public: CompositeEnumerator() : m_eFiltered(TEXT("C:\\")) , m_eCd(TEXT(".")) { } LPCTSTR GetCurDir() { return m_peCur->GetCurDir(); } LPCTSTR GetCurPath() { return m_peCur->GetCurPath(); } const WIN32_FIND_DATA* GetCurFindData() { return m_peCur->GetCurFindData(); } private: void FiberProc(); private: FiberEnumerator* m_peCur; FilteredEnumerator m_eFiltered; DirectoryTreeEnumerator m_eCd; }; void CompositeEnumerator::FiberProc() { FEFOUND fef; m_peCur = &m_eFiltered; while ((fef = m_peCur->Next()) != FEF_DONE && fef != FEF_LEAVEDIR) { m_peCur->SetResult(Produce(fef)); } m_peCur = &m_eCd; while ((fef = m_peCur->Next()) != FEF_DONE) { m_peCur->SetResult(Produce(fef)); } }
Sidebar:
Our composite enumeration is complicated by the
fact that our In the more common case where the enumerator is generating a flat list, it would be a simple matter of just forwarding the two enumerators one after the other. Something like this: void CompositeEnumerator2::FiberProc() { Enum(&m_eFiltered); Enum(&m_eCd); } void CompositeEnumerator2::Enum(FiberEnumerator *pe) { m_peCur = pe; FEFOUND fef; while ((fef = m_peCur->Next()) != FEF_DONE) { m_peCur->SetResult(Produce(fef)); } } End sidebar.
You can try out this CompositeEnumerator e; Exercise: Gosh, why is the total so unusually large? Exercise: How many fibers are there in the program? Exercise: Draw a diagram showing how control flows among the various fibers in this program. Before you get all excited about fibers, consider the following:
I think that's enough about fibers for now. |
Comments (11)
Comments are closed. |
CreateFiberEx is what you would use if you dont want to give the fiber the default stack size. However, it doesnt look to be able to set the stack size for the 1st initial fiber you create.
Finally this boring series is over.
happy :-) you don’t have to read Raymond’s blog if you don’t want to, besides this boring series has some very good info in it that could be useful to many, many people that are happy to get it not happy to see it over with.
This is one of the more useful series of posts lately. I’m thinking of implementing threads in a lisp interpreter and need precise control over the execution of threads (primarily to avoid having threads stomp all over my GC). It seems like fibers might be a good fit.
raymond, thanks for the info on fibers, this was a really informative series.
What exactly do you mean by the "one-thread-per-connection problem"? Connection to what?
Vorn
A quick google for <one thread per connection> yields many hits, among them http://www.cim.mcgill.ca/~franco/OpSys-304-427/lecture-notes/node68.html
Oh that. Yes, I’ve run into that problem.
(one of my first big projects was an IRCd, and I wrote a threadpool thingy for it. Threadpools are easy like whoa in Python, in particular because it’s got queue implementations that come with it that are designed specifically to be nice to threadpools)
The link there doesn’t seem to like me, though.
Vorn
Isn’t the standard solution (when this one hits its limit) to use a fixed size pool of worker threads and have another thread dispatch work to them as available? This usually requires that you treat each connection like a state machine and also works best when the individual activities are fairly fine grained, but I haven’t seen anything that works better yet.
As far as fibers are concerned, they’d probably work great with CreateFiberEx – 1MB is entirely more stack than you need most of the time, and 64k would easily allow 10k connections.
It’s not necessarily another thread dispatching. It’s more often a data structure that makes threads wait until there’s something to do. Check out Python’s PriorityQueue module for an example.
Vorn
A while back there was an article in MSDN magazine about wrapping up the unmaged fibers API to implement…