Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > to const or not to const

Reply
Thread Tools

to const or not to const

 
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      04-09-2010
Jonathan Lee wrote:

> On Apr 9, 12:16 am, Kai-Uwe Bux <(E-Mail Removed)> wrote:
>> there is a difference between physical and logical constness.
>> I could declare the mutex and condition variables mutable.

>
> Sure, but I'm not in the habit of declaring things mutable
> unless I had a really good reason why wait_available()
> _must_ be const. You don't seem to be in that position.


Well, I posed the question because I was pondering alternatives. Currently,
I am leaning toward non-const. (Actually, I am leaning toward eliminating
wait_available() as tonydee has elsethread shown how I can do what I want
without).

>> Huh? Could you please elaborate on this one: you are suggesting an
>> alternative interface, and unfortunately, I don't understand what it
>> should look like.

>
> I just mean something like
>
> Event e = buffer.wait_available(); // accomplishes wait() and pop()
> if (e.valid())
> e.handle();
>
> Nothing big. But since wait_available() is almost always followed
> by a true result from available(), and a pop(), you may as well do
> it all in one. For the few times that wait_available() returns
> and available() is false, just set the returned event to be
> invalid.


Probably, you are focusing on a different issue here: when there are
multiple consumers, wait_available() could return, yet the other consumer
picks the item. This is not, what I am worrying about. I just wanted to be
able to write a loop where I can handle several events and only update the
screen after the last of the available events has been processed. [Use case
(b) from my original post.]

> If you do this, then wait_available() is obviously non-const.



Best

Kai-Uwe Bux
 
Reply With Quote
 
 
 
 
Jonathan Lee
Guest
Posts: n/a
 
      04-09-2010
On Apr 9, 12:40*am, Kai-Uwe Bux <(E-Mail Removed)> wrote:
> Probably, you are focusing on a different issue here: when there are
> multiple consumers, wait_available() could return, yet the other consumer
> picks the item.


Well, I was worried about the fact that most wait() functions can
spuriously return. So wait_available() returning probably does not
guarantee that available() will be true, even with a single
consumer.

I'm pretty sure pthreads and boost both have caveats about spurious
wakeups. Not sure about windows or C++0x...

--Jonathan
 
Reply With Quote
 
 
 
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      04-09-2010
Jonathan Lee wrote:

> On Apr 9, 12:40 am, Kai-Uwe Bux <(E-Mail Removed)> wrote:
>> Probably, you are focusing on a different issue here: when there are
>> multiple consumers, wait_available() could return, yet the other consumer
>> picks the item.

>
> Well, I was worried about the fact that most wait() functions can
> spuriously return. So wait_available() returning probably does not
> guarantee that available() will be true, even with a single
> consumer.


Yeah, I read that in the posix man pages. Hence, my implementation of
wait_available() reads like this:

void wait_available ( void ) {
posix::guard data_guard ( data_lock );
while ( the_data.empty() ) {
data_guard.wait( non_empty );
}
}

For a single consumer thread, the return of the function does indicate that
there is some item waiting.


Best

Kai-Uwe Bux
 
Reply With Quote
 
Balog Pal
Guest
Posts: n/a
 
      04-09-2010
"Kai-Uwe Bux" <(E-Mail Removed)>

> void wait_available ( void ); // should this be const ?
> // blocks until a call to pop() will not block


> But, what about wait_available()? Even though it does not change the
> buffer
> contents by itself, once it returns it effectively signals to the thread
> where it was called that another thread has pushed in item. So it marks a
> change of the buffer, even though it does not in itself bring that change
> about. Should this method be const or not?


Normally my mutex and similar members are declared 'mutable' and functions
messing with them are const (if just do locking). Though a function shall
not leave with a changed state of a mutex (iow, it has a full critical
section), and document locking requirements as preconditions.

OTOH, your wait_available function is fishy as idea -- I can't imagine a
sensible use of it without a race condition introduced. By the time you
call that pop() the queue can be empty again. Or if you left a lock
running, you create an obligation to call one of a set of functions -- I do
that in some rare special cases, but prefer keeping the operation together.
Like a blocking version of pop().


>
>
> Background [use cases in the consumer thread]
>
> a)
> while ( true ) {
> event e = buffer.pop();
> e.handle();
> update_screen();
> }
>
> b)
> while ( true ) {
> buffer.wait_available();
> while ( buffer.available() ) {
> event e = buffer.pop();
> e.handle();
> }
> update_screen();
> }
>
>
> Best
>
> Kai-Uwe Bux
>


 
Reply With Quote
 
tonydee
Guest
Posts: n/a
 
      04-09-2010
On Apr 9, 12:53*pm, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
> > * * value_type pop ( void );
> > * * // blocks until an item is available

>
> This method is very interesting. In a single-threaded program it should clearly
> not be the basic interface but just a convenience wrapper around 'top' and
> 'remove_top', because the copying of value_type may throw after altering the
> fifo state, preventing sound exception guarantee. But in a multi-threaded
> environment how does one use the more primitive operations safely?


Yes, same thoughts came to me, but I was too lazy to address it at the
time....

> I can imagine that 'top' and 'remove_top' would be accessed within a block
> protected by a mutex.
>
> I think that mutex thingy should be exposed as part of the interface, and the
> 'pop' above an inline convenience method.


That sounds workable and clean from the perspective of this issue, but
doesn't a convenience function defeat the point? Sans pop(), it a bit
nasty from a usability perspective.

With a backyard hacker hat on, I might observe a great many copy
operations can't throw (especially if you ignore throws from new(),
discussed below), and pop() has an interesting characteristic: the
potential to "move" the object - which I think C++0x will facilitate
(?) - more efficiently than the Value& top() / void pop() semantics.
The simpler interface may become the better one in just a couple
years.

I remember reading an interesting article about the practicality of
trying to catch exceptions from memory allocation (i.e. new()), which
pointed out that most modern operating systems will do sparse memory
allocation, so unless the address space is smaller than the RAM+swap
(reasonably common for 32-bit apps / not for 64-bit), it's sure to get
the address space, and new won't throw. But, if an attempt is made to
page-fault in some of the memory supposedly allocated, then the "real"
allocation against RAM/swap takes place, and the application may
SIGSEGV. Point was, benefits of doing try/catch around new are
platform specific, and in modern practice you'll probably get more
production issues from more complex/verbose code than you'll
successfully handle during resource exhaustion....

Still, excellent points to bring into the design discussion. Thanks
Alf.

Cheers,
Tony
 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      04-09-2010
* Balog Pal:
> "Alf P. Steinbach" <(E-Mail Removed)>
>> This method is very interesting. In a single-threaded program it
>> should clearly not be the basic interface but just a convenience
>> wrapper around 'top' and 'remove_top', because the copying of
>> value_type may throw after altering the fifo state, preventing sound
>> exception guarantee.

>
> Interesting that this pop/top 'problem' is mentioned so much. In
> practice I can't recall a single case -- the stuff I held in queues and
> stacks just never had a throwing ctor. It was either some simple POD or
> the equivalent of the unique_ptr. Even for single threaded stuff, but in
> MT definitely the latter.
>
> I do not want a fat copy inside a critical section. Let alone one that
> calls system functions for memory and/or may throw.


Yah, the practical solution may just be to require non-throwing copying for the
items.

Anyway, I feel utterly stupid.

I'm looking at <url:
http://code.google.com/codejam/contest/dashboard?c=204113#s=a&a=2>, and they
say, "Behold, the Aha moment ..." -- and there's no Aha in my brain at all.

I'm assuming those colored lines are meant to be directional, after all it's a DAG.

But DAnG!


Cheers,

- Alf
 
Reply With Quote
 
Balog Pal
Guest
Posts: n/a
 
      04-09-2010
"Alf P. Steinbach" <(E-Mail Removed)>
> This method is very interesting. In a single-threaded program it should
> clearly not be the basic interface but just a convenience wrapper around
> 'top' and 'remove_top', because the copying of value_type may throw after
> altering the fifo state, preventing sound exception guarantee.


Interesting that this pop/top 'problem' is mentioned so much. In practice I
can't recall a single case -- the stuff I held in queues and stacks just
never had a throwing ctor. It was either some simple POD or the equivalent
of the unique_ptr. Even for single threaded stuff, but in MT definitely the
latter.

I do not want a fat copy inside a critical section. Let alone one that
calls system functions for memory and/or may throw.

 
Reply With Quote
 
Balog Pal
Guest
Posts: n/a
 
      04-09-2010
"Alf P. Steinbach" <(E-Mail Removed)>
> Anyway, I feel utterly stupid.
>
> I'm looking at <url:
> http://code.google.com/codejam/contest/dashboard?c=204113#s=a&a=2>, and
> they say, "Behold, the Aha moment ..." -- and there's no Aha in my brain
> at all.
>
> I'm assuming those colored lines are meant to be directional, after all
> it's a DAG.


IMO the left graph supposed to have arrows on all lines either up or down.

Tha AHA is probably reserved to those who learned graph theoty and still
remember it -- I don't qualify.

 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      04-09-2010
tonydee wrote:

> On Apr 9, 12:53 pm, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
>> > value_type pop ( void );
>> > // blocks until an item is available

>>
>> This method is very interesting. In a single-threaded program it should
>> clearly not be the basic interface but just a convenience wrapper around
>> 'top' and 'remove_top', because the copying of value_type may throw after
>> altering the fifo state, preventing sound exception guarantee. But in a
>> multi-threaded environment how does one use the more primitive operations
>> safely?

>
> Yes, same thoughts came to me, but I was too lazy to address it at the
> time....
>
>> I can imagine that 'top' and 'remove_top' would be accessed within a
>> block protected by a mutex.
>>
>> I think that mutex thingy should be exposed as part of the interface, and
>> the 'pop' above an inline convenience method.

>
> That sounds workable and clean from the perspective of this issue, but
> doesn't a convenience function defeat the point? Sans pop(), it a bit
> nasty from a usability perspective.
>
> With a backyard hacker hat on, I might observe a great many copy
> operations can't throw (especially if you ignore throws from new(),
> discussed below), and pop() has an interesting characteristic: the
> potential to "move" the object - which I think C++0x will facilitate
> (?) - more efficiently than the Value& top() / void pop() semantics.
> The simpler interface may become the better one in just a couple
> years.


I was thinking along the same lines. Would it be possible in C++0X to write
pop() with a strong exception safety guarantee? Something like: it does the
copy-construction internally and outside a move happens?


> I remember reading an interesting article about the practicality of
> trying to catch exceptions from memory allocation (i.e. new()), which
> pointed out that most modern operating systems will do sparse memory
> allocation, so unless the address space is smaller than the RAM+swap
> (reasonably common for 32-bit apps / not for 64-bit), it's sure to get
> the address space, and new won't throw. But, if an attempt is made to
> page-fault in some of the memory supposedly allocated, then the "real"
> allocation against RAM/swap takes place, and the application may
> SIGSEGV. Point was, benefits of doing try/catch around new are
> platform specific, and in modern practice you'll probably get more
> production issues from more complex/verbose code than you'll
> successfully handle during resource exhaustion....
>
> Still, excellent points to bring into the design discussion.


Well, one could have the following interface:

template < typename T >
class fifo {

public:

typedef T value_type;
typedef std::size_t size_type


fifo ( size_type n = -1 );

void push ( value_type const & value );

class reader {
reader ( fifo & );
~reader ();
value_type const & top ( void ) const;
void pop ( void );
};

bool available ( void ) const;
// returns true if the a call to pop() would not block

void wait_available ( void ); // should this be const ?
// blocks until a call to pop() will not block

};

Now, in the consumer thread, one would have:

...
event e;
{
fifo< event >::reader hold_onto ( buffer );
e = hold_onto.top();
hold_onto.pop();
}
e.handle();
update_screen();
...

The constructor of the reader object would lock the buffer and wait until an
item becomes available. The destructor would unlock.


For the use cases, I have in mind, the above is overkill and makes client
code too messy. So, I think (as Alf suggested) that a convenience function
for non-throwing copy-constructors should be kept.


Best

Kai-Uwe Bux
 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      04-09-2010
Balog Pal wrote:

> "Kai-Uwe Bux" <(E-Mail Removed)>
>
>> void wait_available ( void ); // should this be const ?
>> // blocks until a call to pop() will not block

>
>> But, what about wait_available()? Even though it does not change the
>> buffer
>> contents by itself, once it returns it effectively signals to the thread
>> where it was called that another thread has pushed in item. So it marks a
>> change of the buffer, even though it does not in itself bring that change
>> about. Should this method be const or not?

>
> Normally my mutex and similar members are declared 'mutable' and functions
> messing with them are const (if just do locking). Though a function shall
> not leave with a changed state of a mutex (iow, it has a full critical
> section), and document locking requirements as preconditions.
>
> OTOH, your wait_available function is fishy as idea -- I can't imagine a
> sensible use of it without a race condition introduced. By the time
> you
> call that pop() the queue can be empty again.


If there are multiple consumer threads, that is very true. Therefore, pop()
also blocks until it can return a value. I hoped, the comment for pop()
would make that clear.

> Or if you left a lock
> running, you create an obligation to call one of a set of functions -- I
> do that in some rare special cases, but prefer keeping the operation
> together. Like a blocking version of pop().


Same here.

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
const vector<const MyType> Vs const vector<MyType> magnus.moraberg@gmail.com C++ 2 02-09-2009 10:45 PM
is const necessary in eg int compar(const void *, const void *) lovecreatesbeauty@gmail.c0m C Programming 26 11-10-2008 09:47 PM
const correctness - should C++ prefer const member over non-const? fungus C++ 13 10-31-2008 05:33 AM
const vector<A> vs vector<const A> vs const vector<const A> Javier C++ 2 09-04-2007 08:46 PM
Casting int'** to 'const int * const * const' dosn't work, why? Jonas.Holmsten@gmail.com C Programming 11 07-01-2007 06:16 PM



Advertisments