
Sorry, I'm a little late - here goes: Anthony Williams wrote:
The current C++0x thread library proposal (http://www.open- std.org/jtc1/sc22/wg21/docs/papers/2007/n2320.html) includes request_cancellation(), which requests a thread cancel at the next call to cancellation_point() or one of the standard cancellation points (such as waiting on a condition variable). I intend to add this facility to Boost.thread once the move to subversion is complete.
Yes - and it's useful. I would like to see both possibili- ties! The cancellation by "polling" is most useful when writing server- or lib-code. A GUI friendly approach has to be different however. If you have to poll anyway - why go through all the thread trouble anyhow - just poll the GUI event loop instead. Creating worker threads is usually done for convenience. Polling is inconvenient. I read the proposal. One thing that stroke me was the list of recommended cancellation points. They all somehow imply that the thread is already "on hold" (if not polling). Let's look at real life examples: 1. You have an editor or whatever application; the user has selected a file that is to be read in. For convenience, the reading takes place in a background thread. The user has the possibility to cancel the operation. Suppose the file is large and being read over a dead slow NFS connection. So (s)he chooses to cancel. Where is you cancellation point in there? Is the application supposed to get stuck (as many do)? 2. The classical worker thread pattern: You have a really tight loop computing something. You just don't want to poll in there as it hurts. The com- pare operation for checking out of the loop every once in a while already costs too much. What do you do? Currently only "dirty killing" is what I could think of and that usually means that you have to do the cleanup in the main thread (not object-oriented). Those patterns can be found in many (if not the majority) of GUI applications. Sure, you can write around it (see Qt) - but that's rather ugly coding...
This is very messy --- it's asynchronous, so will break all sorts of stuff. POSIX asynchronous thread cancellation has successfully demonstrated that it's a bad idea, except for very specific cases.
Posix' threads came late - too late. And as usual they didn't really go for a "pragmatic approach". The old saying: "Posix is better than nothing". I agree that it's a bit messy behind the scenes. However, you can have it the easy way. If you put the thread to sleep first - your patient is in a coma; operating on it is not so tricky then. That was only a bare example, you can of course synchronize it (and you might want to do that in many cases;o). Again, let's take a look at an example: A trickier one - cancellation while retaining data: For example the application has computed for hours and you want to abort the operation and let it finish tomorrow 'coz you need to phone ant Hermine and you only have one line... Silly code example: // either declared in the object or on the heap: map< BigUInt, answer_type > *war_dial; // then in the thread you have to put those // critical accesses within a "critical section": noAbortBegin(); war_dial[ number ] = answer; noAbortEnd(); // or for the forgetful as an object: { noAbort na; war_dial[ number ] = answer; } Behind the scenes this basically increments an atomic variable (check and set). So if you have a cancellation request from another thread (say the main thread), the worker thread is put to sleep. Then the no-abort variable is checked, if not zero a flag is set to indicate to the thread to throw an abort-request itself the next time it ends a "critical section" and the counter is zero (nesting). If the no-abort is unchecked (count of zero) you can fix the instruction pointer to let the thread throw an abort-request when it wakes up. Then you wake the sleeping thread and either wait for it to die, wait with a timeout - or (in case it's all on the heap) simply not care anymore. For the latter you have to make sure not to leave any children running while the parents go dead of course... Very much like C++ exceptions this is not that efficient. It's meant for exceptions - and only exceptions. "Abort requested by user" is one of those (and a very common one too). So I would still like to see both approaches implemented (they complement one another). The possibility to cause an exception within a thread no matter what operation it's doing is very powerful. Asking a programmer to protect critical accesses on persistent data/objects with "critical sections" if using that feature is not too much (and incrementing a variable is rather cheap as well). Hope I don't seem to stubborn about this, but I really think that once it would be there C++/C-0x programmers wouldn't want to miss such a feature anymore. --- about wx: Scott McMurray wrote:
I read that some folks thought about the wxWidgets and the Boost (C-0x) team joining forces. I think that would be a marvelous idea!
I think they'd come to blows, given how reluctant wx seems to be to use any feature added to C++ in the past 17 years or so :|
I don't think that's an ezaggeration, either. Have you looked at the wx developer guidelines lately? Looks like they want nothing from after the ARM...
Hahaha, yes - arrays, maps and the like as preprocessor macros... also when it comes to posting bug reports it seems very much like someone there is "running a camp"! X-O It is supposed to be about "backward compatibility" with some compilers from the Jurassic period. However, it looks as if "they" intent to start over with version 3. C-0x is going to end up in the same swamp as C++ without a useable GUI support. Sure, you can have second party libraries. But then you also need support for the second party libs (like GUI builders, usually not integrated) and the swamp sets in deep when debugging non-native objects. "Do it by hand Luke"... Like this, C-0x won't stand a chance against free all-in-one Java or C# (reads: "C crooked") alternatives (not that I would use them - coming from the Assembler countries). Despite the reasonable disbelieves I think that there should be a debate with the wx folks. The problem with C-0x seems to be a lack of resources for something as complex as a proper GUI support. With the wx team maybe willing to start all over (they certainly didn't do a bad job!) joining forces could bring C-0x to new highs. It's worth a try, someone cast the first stone PLEASE! :-)
... Last I looked, gtkmm was the closest to providing C++- style interfaces and such, so maybe there's hope.
Hmmm... I think the mingw part is somewhat chasing away programmers and the last time I looked into gtkmm it read "TODO" almost everywhere... Same problem as C-0x it seems. ;-{
Unfortunately, http://www.omgui.org/, which was trying for a real C++ GUI lib, seems rather dead.
Lack of resources - how I hate this song...
The STL is also quite useful, all by itself. I'm using it heavily in my research. I have no use for GUIs or sockets. I tried threads, but my bottlenecks are all I/O, so they don't make a difference. I do most of my the output to console, with some OpenGL for visualization. No QT, wx, or other ancient frameworks needed, nor would they be helpful.
If the bottlenecks are all I/O then the STL (iostreams in particular) could be one of them. If I recall it correctly the STL was a lot slower than the C counterparts in that area. X-| Well, that's it from me - gotta puff up my belly... Regards, LC (myLC@gmx.de) -- Psssst! Schon vom neuen GMX MultiMessenger gehört? Der kanns mit allen: http://www.gmx.net/de/go/multimessenger

"Ninel Evol" <myLC@gmx.de> writes:
Anthony Williams wrote:
The current C++0x thread library proposal (http://www.open- std.org/jtc1/sc22/wg21/docs/papers/2007/n2320.html) includes request_cancellation(), which requests a thread cancel at the next call to cancellation_point() or one of the standard cancellation points (such as waiting on a condition variable). I intend to add this facility to Boost.thread once the move to subversion is complete.
Yes - and it's useful. I would like to see both possibili- ties! The cancellation by "polling" is most useful when writing server- or lib-code. A GUI friendly approach has to be different however. If you have to poll anyway - why go through all the thread trouble anyhow - just poll the GUI event loop instead. Creating worker threads is usually done for convenience. Polling is inconvenient.
I read the proposal. One thing that stroke me was the list of recommended cancellation points. They all somehow imply that the thread is already "on hold" (if not polling). Let's look at real life examples: 1. You have an editor or whatever application; the user has selected a file that is to be read in. For convenience, the reading takes place in a background thread. The user has the possibility to cancel the operation. Suppose the file is large and being read over a dead slow NFS connection. So (s)he chooses to cancel. Where is you cancellation point in there? Is the application supposed to get stuck (as many do)?
The platform is free to provide additional cancellation points if required. However, there is no guaranteed cancellation point in any other operation, as this would require modifying the OS-supplied functions.
2. The classical worker thread pattern: You have a really tight loop computing something. You just don't want to poll in there as it hurts. The com- pare operation for checking out of the loop every once in a while already costs too much. What do you do? Currently only "dirty killing" is what I could think of and that usually means that you have to do the cleanup in the main thread (not object-oriented).
If you really feel you have no choice then get the native_handle() for the thread and terminate your thread using platform APIs. Testing for cancellation shouldn't be expensive, but I agree that it will cost CPU time.
Those patterns can be found in many (if not the majority) of GUI applications. Sure, you can write around it (see Qt) - but that's rather ugly coding...
Yes. Please bear in mind that this is a base-level proposal. TR2 will build on this proposal, as will later versions of the C++ Standard. Additional cancellation points may be added, particularly in IO operations.
This is very messy --- it's asynchronous, so will break all sorts of stuff. POSIX asynchronous thread cancellation has successfully demonstrated that it's a bad idea, except for very specific cases.
Posix' threads came late - too late. And as usual they didn't really go for a "pragmatic approach". The old saying: "Posix is better than nothing".
In this case, POSIX is worse than nothing.
I agree that it's a bit messy behind the scenes. However, you can have it the easy way. If you put the thread to sleep first - your patient is in a coma; operating on it is not so tricky then. That was only a bare example, you can of course synchronize it (and you might want to do that in many cases;o).
That's not the problem. The problem comes for the thread being cancelled --- if you cancel it asynchronously, it may be in the middle of any operation, including those that normally can't fail, such as an unsigned integer addition. If your code may be cancelled asynchronously, you need to assume every operation can fail, and that makes correct and robust programming essentially impossible for any suitably complex task. It can be made to work for small regions, but not for large regions of code. Please bear in mind that the proposed cancellation makes the cancelled thread throw a normal C++ exception from a call to cancellation_point(). Asynchronous cancellation would cause that exception to be thrown at unpredictable places.
Again, let's take a look at an example: A trickier one - cancellation while retaining data: For example the application has computed for hours and you want to abort the operation and let it finish tomorrow 'coz you need to phone ant Hermine and you only have one line... Silly code example:
// either declared in the object or on the heap: map< BigUInt, answer_type > *war_dial;
// then in the thread you have to put those // critical accesses within a "critical section": noAbortBegin(); war_dial[ number ] = answer; noAbortEnd();
// or for the forgetful as an object: { noAbort na; war_dial[ number ] = answer; }
Behind the scenes this basically increments an atomic variable (check and set). So if you have a cancellation request from another thread (say the main thread), the worker thread is put to sleep. Then the no-abort variable is checked, if not zero a flag is set to indicate to the thread to throw an abort-request itself the next time it ends a "critical section" and the counter is zero (nesting). If the no-abort is unchecked (count of zero) you can fix the instruction pointer to let the thread throw an abort-request when it wakes up. Then you wake the sleeping thread and either wait for it to die, wait with a timeout - or (in case it's all on the heap) simply not care anymore. For the latter you have to make sure not to leave any children running while the parents go dead of course...
If war_dial is not a cancellation point, just put a call to cancellation_point() before and after the call. If war_dial *is* a cancellation point, but you don't want it to cancel, use a std::disable_cancellation object to disable cancellation across the call.
The possibility to cause an exception within a thread no matter what operation it's doing is very powerful.
Too powerful.
Asking a programmer to protect critical accesses on persistent data/objects with "critical sections" if using that feature is not too much (and incrementing a variable is rather cheap as well).
Yes, it is. It is all too easy to forget one "critical section", especially in C++ where lots of operations are implicit.
Hope I don't seem to stubborn about this, but I really think that once it would be there C++/C-0x programmers wouldn't want to miss such a feature anymore.
I am strongly opposed to asynchronous exception injection and asynchronous cancellation. Anthony -- Anthony Williams Just Software Solutions Ltd - http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL
participants (2)
-
Anthony Williams
-
Ninel Evol