Friday, February 20, 2015

Digest for comp.lang.c++@googlegroups.com - 9 updates in 2 topics

Christopher Pisz <nospam@notanaddress.com>: Feb 19 06:54PM -0600

First, I just wanted to take a second and say that interruption points
on threads are wonderful...so far
 
I am using boost, because my employer is still requiring I support XP,
and this msvc2010, which doesn't seem to have C++11 threads, but as I
understand it they are almost identical.
 
If I interrupt a worker thread from my main thread, it throws a
thread_interrupted exception from any "interruption points". For my
purposes, they will be sleeps.
 
So, my worker thread must wrap all of its work in a try catch and can
for example's sake, log that it was interrupted, and exit in the catch.
 
This makes for a great way to shut everything down.
 
What I am worried about is, there are sleeps all over the place in the
worker thread, in many many classes. Am I going to have to go through
each class (I didn't write) and make sure they do something sane in
their destructors as the exception makes its way up the call stack? or
alternatively insert a lot of catch statements and try to handle things
there before rethrowing?
 
What are other interruption points that I should look at?
 
-----
I have chosen to troll filter/ignore all subthreads containing the
words: "Rick C. Hodgins", "Flibble"
So, I won't be able to see or respond to any such messages
-----
Robert Wessel <robertwessel2@yahoo.com>: Feb 19 10:59PM -0600

On Thu, 19 Feb 2015 18:54:59 -0600, Christopher Pisz
>their destructors as the exception makes its way up the call stack? or
>alternatively insert a lot of catch statements and try to handle things
>there before rethrowing?
 
 
Pretty much yes.
 
But as far as I'm concerned, that's the wrong way to deal with things
like shutdown. Interruptions are too low level to be useful. Just
because you're in a wait doesn't mean you're in a convenient place to
shut the thread down. Sending a message of some sort to a thread to
shut down, which the thread checks for at reasonable points, makes
much more sense. An event semaphore is often useful. On those waits
where you want to allow shutdown, you can wait for the shutdown
semaphore in addition to the other items. And where it's appropriate,
you can poll it with a timed wait (with a zero timeout).
Luca Risolia <luca.risolia@linux-projects.org>: Feb 20 12:01PM +0100

On 20/02/2015 01:54, Christopher Pisz wrote:
> I am using boost, because my employer is still requiring I support XP,
> and this msvc2010, which doesn't seem to have C++11 threads, but as I
> understand it they are almost identical.
 
No, boost and the current C++ standard are not identical with respect to
interruptible threads, as there is no concept of "thread interruption"
in the standard (of course, this does not mean you cannot implement it
with the tools present in the standard library).
 
> Am I going to have to go through
> each class (I didn't write) and make sure they do something sane in
> their destructors as the exception makes its way up the call stack?
 
Your question has probably nothing to do with interruptible threads in
particular. Destructors should never throw (except in one or two rare
cases where it is known to be safe), if this is what you meant.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Feb 20 01:18PM

On Thu, 19 Feb 2015 22:59:59 -0600
> where you want to allow shutdown, you can wait for the shutdown
> semaphore in addition to the other items. And where it's appropriate,
> you can poll it with a timed wait (with a zero timeout).
 
I don't agree with that. I find thread interruption/cancellation
useful for some cases, provided you choose the interruption/
cancellation point by blocking interruption/cancellation by default. If
you are using POSIX or boost threads, there is little point in
reinventing the wheel with your own event semaphores to achieve the
same end. (And busy waiting on a zero timeout is also highly
undesirable.) When using POSIX threads then, subject to blocking,
recent versions of the BSDs, linux since kernel 2.6 and all modern
commercial unixes will unwind the stack on cancellation via a
pseudo-exception, which is of course what you want. (Boost, which
implements interruption at the library level, will also unwind the
stack.)
 
The key is (i) always used deferred cancellation, and (ii) block
cancellation/interruption by default so you control in _your_ code the
points where cancellation/interruption is active. This avoids the
problems alluded to by the OP. Since windows threads don't offer that
by themselves, with windows native threads you have to resort to your
kind of approach if you are not using boost. That may be where your
background comes from.
 
The main problem with boost thread interruption is the paucity of
interruption points. That is inevitable with a library level solution
such as boost, because at the library level you cannot easily interrupt
blocking system calls. The same applies to your technique. POSIX on
the other hand makes most blocking system calls also thread
cancellation points. This makes thread interruption/cancellation
with C++ only a really useful technique with POSIX/unix-like
platforms. This is also why C++11/14 does not provide for thread
interruption, and probably never will.
 
Chris
scott@slp53.sl.home (Scott Lurndal): Feb 20 02:41PM

>where you want to allow shutdown, you can wait for the shutdown
>semaphore in addition to the other items. And where it's appropriate,
>you can poll it with a timed wait (with a zero timeout).
 
Something like this, perhaps (d_exit is marked volatile):
 
/**
* Execute I/O Control Blocks queued to this DLP instance.
*
* This is the main loop of the per-DLP instance thread.
*
* Loop until the d_exit flag is set to true.
* Set d_exited flag when the thread terminates to notify
* terminator that the thread terminated successfully.
*/
void
c_dlp::run(void)
{
int diag;
c_dlist pending;
char tname[64];
 
c_processor::set_this(d_processor);
c_system::set_this(mp->get_system());
c_memory::set_this(mp->get_system()->get_mem());
 
snprintf(tname, sizeof(tname), "%04lu %s DLP",
d_channel, get_hardware_name());
set_threadname(tname);
 
pending.init();
 
lock_thread();
while (!d_exit) {
while (pending.is_empty()) {
c_dlist_iterator di(&d_iocbs);
while (di.next()) {
c_iocb *iocb = (c_iocb *)di.curr();
 
iocb->remove();
pending.insert(iocb);
}
if (!pending.is_empty()) break;
diag = pthread_cond_wait(&d_wait, &t_threadlock);
if (diag != 0) {
d_logger->log("%04lu/00 Unexpected cond-wait result: %s\n",
d_channel, strerror(diag));
}
if (d_exit) break;
}
 
if (d_exit) break;
 
unlock_thread();
c_dlist_iterator worklist(&pending);
while (worklist.next()) {
c_iocb * iocb = (c_iocb *)worklist.curr();
iocb->remove();
 
switch (iocb->get_op()) {
case IOD_CANCEL:
cancel(iocb);
break;
case IOD_READ:
read(iocb);
break;
case IOD_WRITE:
write(iocb);
break;
case IOD_TEST:
if (iocb->get_op_var1() == IOD_TEST_IDENTIFY) {
set_rd(iocb, IOT_COMPLETE|IOT_EXT_RD_PRESENT,
0x0000, d_testid << 8);
} else {
test(iocb);
}
break;
case IOD_ECHO:
echo(iocb);
break;
}
}
lock_thread();
pending.init();
}
d_exited = true;
 
unlock_thread();
}
 
/**
* Destructor for a Data Link Processor. Tell the device thread
* to exit and wait for it to terminate.
*/
c_dlp::~c_dlp(void)
{
 
lock_thread();
 
d_exit = true;
pthread_cond_signal(&d_wait);
 
unlock_thread();
 
if (!in_context()) {
(void) join();
}
}
 
/**
* Queue an IOCB to a DLP. Used by the SPIO instruction to deliver
* an IOCB to a DLP.
*
* Called from a processor thread.
*
* @param iocb The IOCB to deliver.
*/
void
c_dlp::queue(c_iocb *iocb)
{
if (mp->get_system()->is_iotrace(iocb->get_channel())) {
iocb->dump(d_logger, "Queue ", false);
}
 
lock_thread();
d_iocbs.insert(iocb);
pthread_cond_signal(&d_wait);
unlock_thread();
if (mp->get_host_cores() == 1) {
sched_yield(); // Let the DLP thread run
}
}
Paavo Helde <myfirstname@osa.pri.ee>: Feb 20 12:52PM -0600

Christopher Pisz <nospam@notanaddress.com> wrote in news:mc60js$ksf$1@dont-
email.me:
 
> Am I going to have to go through
> each class (I didn't write) and make sure they do something sane in
> their destructors as the exception makes its way up the call stack?
 
Pretty much any C++ code piece can throw at least std::bad_alloc, so all
code should be exception safe anyway. If you have so far not cleaned up
your code base to have proper RAII and exception safety, then it's last
time to do it.
 
Cheers
Paavo
Marcel Mueller <news.5.maazl@spamgourmet.org>: Feb 20 10:46PM +0100

On 20.02.15 19.52, Paavo Helde wrote:
> Pretty much any C++ code piece can throw at least std::bad_alloc, so all
> code should be exception safe anyway.
 
Well, pretty much any existing C++ application will never recover from
an allocation error in a reasonably way. Simply because almost
everything requires some successful memory allocations. Even if only for
allocation of the std::string with the error message.
 
> If you have so far not cleaned up
> your code base to have proper RAII and exception safety, then it's last
> time to do it.
 
This might be a good advise anyway.
 
 
Marcel
Luca Risolia <luca.risolia@linux-projects.org>: Feb 20 11:44PM +0100

Il 20/02/2015 22:46, Marcel Mueller ha scritto:
> On 20.02.15 19.52, Paavo Helde wrote:
> Well, pretty much any existing C++ application will never recover from
> an allocation error in a reasonably way.
 
C++ provides you a reasonable way.
 
> Simply because almost
> everything requires some successful memory allocations.
 
This is one of the purposes of the new-handler function. See
std::set_new_handler.
StuartRedmann <DerTopper@web.de>: Feb 20 04:10PM +0100

Am 18.02.15 um 15:03 schrieb Stefan Ram:
 
> can be thought of as a field-by-field copy (at least in the
> simple case of POD-like objects) from object1 to object0
> that does not change the existence status of object0 or object1.
 
That is the standard implementation of the copy constructor/operator=.
However, the intention of operator= is that it should behave in such a
way that two objects have the same behaviour after an assignment (at
least with respect to the problem domain).
 
I wonder, I wonder. Someone must have hacked into Stefan Ram's account.
I wouldn't have expected such a question from Stefan...
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: