Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Found a use case for `const` by value return, a shared queue

145 views
Skip to first unread message

Alf P. Steinbach

unread,
Feb 4, 2023, 4:32:30 PM2/4/23
to
I finally encountered a use for `const T` as function return type.

This code appears to work well for the single purpose it's been used
for, but it's not tested or even used extensively. And that includes
that I haven't even tested/used that `const T` return function yet. But:


template< class Item >
class Shared_queue_
{
queue<Item> m_queue;

mutable mutex m_mutex;
mutable condition_variable m_condition;

auto length() const -> int { return int_size_of( m_queue ); }
auto is_empty() const -> bool { return m_queue.empty(); }

public:
class Access:
public Movable
{
friend class Shared_queue_;

Shared_queue_* m_p_queue;
Mutex_lock m_mutex_lock;

Access( Shared_queue_& q ):
m_p_queue( &q ),
m_mutex_lock( q.m_mutex )
{}

Access( const Shared_queue_& q ):
m_p_queue( &q ),
m_mutex_lock( q.m_mutex )
{}

public:
auto length() const -> int { return m_p_queue->length(); }
auto is_empty() const -> bool { return
m_p_queue->is_empty(); }

void wait_for_items()
{
if( not is_empty() ) { return; }
m_p_queue->m_condition.wait( m_mutex_lock, [&]() ->
bool { return not is_empty(); } );
}

auto wait_for_items_for( const Duration duration )
-> bool
{
if( not is_empty() ) { return true; }
// TODO: handle possible overflow in
`steady_clock::now() + duration`.
const bool timed_out = not m_p_queue->m_condition.wait_for(
m_mutex_lock, duration, [&]() -> bool { return not
is_empty(); }
);
return not timed_out;
}

void enq( Item item, const bool notify = true )
{
m_p_queue->m_queue.push( move( item ) );
if( notify ) { m_p_queue->m_condition.notify_one(); }
}

auto deq() -> Item
{
wait_for_items();
hopefully( not is_empty() )
or FSM_FAIL( "Waited for items but got none; queue
is empty." );
return popped_front_of( m_p_queue->m_queue );
}
};

auto access() -> Access { return Access( *this ); }
auto access() const -> const Access { return Access( *this ); }
};


- Alf

Alf P. Steinbach

unread,
Feb 5, 2023, 9:38:02 AM2/5/23
to
Right after I went to bed last night it hit me that the `const` `access`
function couldn't possibly work as written, because the `Access`
constructor it calls tries to initialize a `Shared_queue_*` with a
`const Shared_queue_*`.

I haven't yet tried to call it (functions in templates can be very wrong
so that they wouldn't compile if used, as long as they're not actually
used), but after two cups of coffee I now think the same.

I saw two possible solutions before I went to sleep:

* use a union for the proxy object's pointer-to-queue, or
* use separate proxy types for the mutable and const cases.

I am not at all sure that type punning `const T*` and `T*` via a union,
even if it is dynamically safe and correct from a machine code point of
view, is formally allowed in C++?

With separate proxy types a `Mutable_access_` can inherit from a
`Const_access_` and have the pointer type or constness thereof as a
template parameter, but though it's type-wise clean it also feels like a
kludge; complex. Furthermore the `const` on the return type will then
not matter. So is it that with formally correct C++ I have not run into
a case where `const` on a by-value return type is reasonable?


- Alf

Chris M. Thomasson

unread,
Feb 5, 2023, 3:49:25 PM2/5/23
to
[...]

Signalling a condition variable while the mutex is locked is not that
efficient unless the condvar has wait morphing. God, I have not thought
about wait morphing for a long time. Thanks for making me think of it!



Alf P. Steinbach

unread,
Feb 5, 2023, 4:40:15 PM2/5/23
to
Thanks. I don't know yet how to handle that with the RAII-based access
scheme. Perhaps set a count of number of signals to issue then do that
in the Access destructor.

All this C++ multithreading is fairly new to me.

Previous experience mainly limited to (1) implementing multitasking in
6502 or something assembly at school in 1984 or so, using an HP 64000
development system (that was really really cool!), and implementing
`longjmp` based coroutines in a very very unsafe way in the 1990's,
unfortunately with the code picked up and used by a hospital in New
York, which made me really scared of what I might be responsible for.
Oh, and I forgot, also at school in 1984 or so, an Othello game where
the computer found its next most likely moves while the user was
thinking of his/her move. Due to the apparently instant moves of the
program (it had had much time to think, invisibly) my teacher at first
refused to believe it was not cheating of some kind; I had to show the
debug output of the thinking process on a separate terminal...

- Alf

Alf P. Steinbach

unread,
Feb 5, 2023, 4:45:48 PM2/5/23
to
I landed on a simple type safe solution with a bit of overhead.

Namely using a `std::variant` for the proxy's pointer to its referent.


template< class Item >
class Shared_queue_
{
queue<Item> m_queue;

mutable mutex m_mutex;
mutable condition_variable m_condition;

auto length() const -> int { return int_size_of( m_queue ); }
auto is_empty() const -> bool { return m_queue.empty(); }

public:
class Access:
public Movable
{
friend class Shared_queue_;
using Q_ptr = variant<Shared_queue_*, const Shared_queue_*>;

Q_ptr m_p_queue;
Mutex_lock m_mutex_lock;

Access( Shared_queue_& q ): m_p_queue( &q ), m_mutex_lock(
q.m_mutex ) {}
Access( const Shared_queue_& q ): m_p_queue( &q ),
m_mutex_lock( q.m_mutex ) {}

auto shared_queue() -> Shared_queue_& { return
*get<Shared_queue_*>( m_p_queue ); }

auto shared_queue() const
-> const Shared_queue_&
{ return *(m_p_queue.index() == 0? get<0>( m_p_queue ) :
get<1>( m_p_queue )); }

public:
auto length() const -> int { return
shared_queue().length(); }
auto is_empty() const -> bool { return
shared_queue().is_empty(); }

void wait_for_items()
{
if( not is_empty() ) { return; }
shared_queue().m_condition.wait( m_mutex_lock, [&]() ->
bool { return not is_empty(); } );
}

auto wait_for_items_for( const Duration duration )
-> bool
{
if( not is_empty() ) { return true; }
// TODO: handle possible overflow in
`steady_clock::now() + duration`.
const bool timed_out = not
shared_queue().m_condition.wait_for(
m_mutex_lock, duration, [&]() -> bool { return not
is_empty(); }
);
return not timed_out;
}

void enq( Item item, const bool notify = true )
{
shared_queue().m_queue.push( move( item ) );
if( notify ) { shared_queue().m_condition.notify_one(); }
}

auto deq() -> Item
{
wait_for_items();
hopefully( not is_empty() )
or FSM_FAIL( "Waited for items but got none;
shared_queue is empty." );
return popped_front_of( shared_queue().m_queue );
}
};

auto access() -> Access { return Access( *this ); }
auto access() const -> const Access { return Access( *this ); }
};

- Alf


Alf P. Steinbach

unread,
Feb 6, 2023, 4:09:49 PM2/6/23
to
On 2023-02-05 10:39 PM, Alf P. Steinbach wrote:
> On 2023-02-05 9:49 PM, Chris M. Thomasson wrote:
>
>> Signalling a condition variable while the mutex is locked is not that
>> efficient unless the condvar has wait morphing. God, I have not
>> thought about wait morphing for a long time. Thanks for making me
>> think of it!
>
> Thanks. I don't know yet how to handle that with the RAII-based access
> scheme. Perhaps set a count of number of signals to issue then do that
> in the Access destructor.

Implementation of that idea, hopefully it makes sense and is technically
correct:

template< class Item >
class Shared_queue_
{
queue<Item> m_queue;

mutable mutex m_mutex;
mutable condition_variable m_condition;

auto length() const -> int { return int_size_of( m_queue ); }
auto is_empty() const -> bool { return m_queue.empty(); }

public:
class Access:
public Movable
{
friend class Shared_queue_;
using Q_ptr = variant<Shared_queue_*, const Shared_queue_*>;

Q_ptr m_p_queue;
Mutex_lock m_mutex_lock;
int m_n_notifications = 0;

Access( Shared_queue_& q ): m_p_queue( &q ), m_mutex_lock(
q.m_mutex ) {}
Access( const Shared_queue_& q ): m_p_queue( &q ),
m_mutex_lock( q.m_mutex ) {}

auto shared_queue() -> Shared_queue_& { return
*get<Shared_queue_*>( m_p_queue ); }

auto shared_queue() const
-> const Shared_queue_&
{ return *(m_p_queue.index() == 0? get<0>( m_p_queue ) :
get<1>( m_p_queue )); }

public:
~Access()
{
m_mutex_lock.unlock();
repeat_times( m_n_notifications, [&]{
shared_queue().m_condition.notify_one();
} );
}

auto length() const -> int { return
shared_queue().length(); }
auto is_empty() const -> bool { return
shared_queue().is_empty(); }

void wait_for_items()
{
if( not is_empty() ) { return; }
shared_queue().m_condition.wait( m_mutex_lock, [&]() ->
bool { return not is_empty(); } );
}

auto wait_for_items_for( const Duration duration )
-> bool
{
if( not is_empty() ) { return true; }
// TODO: handle possible overflow in
`steady_clock::now() + duration`.
const bool timed_out = not
shared_queue().m_condition.wait_for(
m_mutex_lock, duration, [&]() -> bool { return not
is_empty(); }
);
return not timed_out;
}

void enq( Item item, const bool notify = true )
{
shared_queue().m_queue.push( move( item ) );
m_n_notifications += notify;
}

auto deq() -> Item
{
wait_for_items();
hopefully( not is_empty() )
or FSM_FAIL( "Waited for items but got none;
shared_queue is empty." );
return popped_front_of( shared_queue().m_queue );

Scott Lurndal

unread,
Feb 6, 2023, 4:35:46 PM2/6/23
to
"Alf P. Steinbach" <alf.p.s...@gmail.com> writes:
>On 2023-02-05 10:39 PM, Alf P. Steinbach wrote:
>> On 2023-02-05 9:49 PM, Chris M. Thomasson wrote:
>>
>>> Signalling a condition variable while the mutex is locked is not that
>>> efficient unless the condvar has wait morphing. God, I have not
>>> thought about wait morphing for a long time. Thanks for making me
>>> think of it!
>>
>> Thanks. I don't know yet how to handle that with the RAII-based access
>> scheme. Perhaps set a count of number of signals to issue then do that
>> in the Access destructor.
>
>Implementation of that idea, hopefully it makes sense and is technically
>correct:

Leaving aside the odd syntactic choices, what is the function
of the class and why would it be of use? How about providing
an example of use?

I don't recall 'not', 'and' and 'or' being C++ keywords.

What's the difference between "mutex" and "Mutex_lock" types?

Where is the mutex actually acquired? It is released in the
destructor - does the undefined "Mutex_lock" class acquire the
mutex in some misguided attempt to apply RAII to locking?

IME, a critical region generally protects more just queue accesses;
it may also protect other data or make a certain code sequence atomic
with respect to other threads. That's one of the many reasons that
I dislike the application of RAII to locking.

Whatever agent you used to post this wrapped many of the longer lines
which made it even less readable than your nonstandard syntax choices.

m_mutex.lock();
qe = m_queue.pop()
m_mutex_unlock();

seems to be much cleaner and far more readable (and thus more maintainable).

The idea that locking can be solved using RAII universally is unviable.

/**
* 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();

Alf P. Steinbach

unread,
Feb 6, 2023, 5:44:54 PM2/6/23
to
On 2023-02-06 10:35 PM, Scott Lurndal wrote:
> "Alf P. Steinbach" <alf.p.s...@gmail.com> writes:
>> On 2023-02-05 10:39 PM, Alf P. Steinbach wrote:
>>> On 2023-02-05 9:49 PM, Chris M. Thomasson wrote:
>>>
>>>> Signalling a condition variable while the mutex is locked is not that
>>>> efficient unless the condvar has wait morphing. God, I have not
>>>> thought about wait morphing for a long time. Thanks for making me
>>>> think of it!
>>>
>>> Thanks. I don't know yet how to handle that with the RAII-based access
>>> scheme. Perhaps set a count of number of signals to issue then do that
>>> in the Access destructor.
>>
>> Implementation of that idea, hopefully it makes sense and is technically
>> correct:
>
> Leaving aside the odd syntactic choices, what is the function
> of the class and why would it be of use? How about providing
> an example of use?
>
> I don't recall 'not', 'and' and 'or' being C++ keywords.

I do. ;-)

https://eel.is/c++draft/lex.digraph


> What's the difference between "mutex" and "Mutex_lock" types?

using Mutex_lock = std::unique_lock<std::mutex>;

That is, a `Mutex_lock` is the combination of a lock and a mutex, as the
name implies.

But now that you mention it, I realize the standard's terminology is not
really clear. It should be a "locker", not a "lock". Yes.


> Where is the mutex actually acquired? It is released in the
> destructor - does the undefined "Mutex_lock" class acquire the
> mutex in some misguided attempt to apply RAII to locking?

Yes it's RAII, and `std::unique_lock` is the standard library's RAII
based wrapper for a mutex lock: it acquires a lock in its constructor.

I wouldn't call the standard library's threading support misguided.

Not because I know enough about the technical to evaluate it, but
because I know that it's designed by some of the finest & best, and it
worked fine for me.


> IME, a critical region generally protects more just queue accesses;
> it may also protect other data or make a certain code sequence atomic
> with respect to other threads. That's one of the many reasons that
> I dislike the application of RAII to locking.
>
> Whatever agent you used to post this wrapped many of the longer lines

The agent is Mozilla Thunderbird.

It uses a line continuation scheme that's specified in RFC 3676 section
4.1, of putting a space at the end of a line as continuation symbol;
<url: https://www.ietf.org/rfc/rfc3676.txt>.

That's called a "soft line break", and the scheme is called "flowed
format", which you can see in the posting's headers.

Thus, looking at my posting in TB's usual view it's fine, but the raw
posted text lines are wrapped.

Evidently your newsreader XRN (X news reader) or perhaps your version of
that newsreader, doesn't support RFC 3676's flowed format.


> which made it even less readable than your nonstandard syntax choices.
>
> m_mutex.lock();
> qe = m_queue.pop()
> m_mutex_unlock();
>
> seems to be much cleaner and far more readable (and thus more maintainable).

Yes, and in this case it would be sufficiently safe.

But in general it's not exception safe.

Letting a destructor do the unlocking is exception safe, and very
convenient. :)


> The idea that locking can be solved using RAII universally is unviable.

That may be, I don't know enough yet to have an opinion. Sorry for being
incompetent in this matter. :-o But it served its purpose well for the
shared queue.


[snip code example]


- Alf

Paavo Helde

unread,
Feb 6, 2023, 6:03:09 PM2/6/23
to
06.02.2023 23:35 Scott Lurndal kirjutas:
>
> The idea that locking can be solved using RAII universally is unviable.

Right, but in C++ we have the means and habits to encapsulate such
non-RAII things in low-level classes like
std::condition_variable::wait() et al, so the application level code can
be clean and RAII.


Scott Lurndal

unread,
Feb 6, 2023, 8:28:03 PM2/6/23
to
There are simple cases where that approach is sufficient. But as I
noted above, it is not suitable for all cases. I've found it (RAII) more
a hindrence than an aid in most real world (locking) cases. Either it forces
clumsy/exessive source code or it expands the size of the critical region; the former
hinders maintenance, the latter, performance. I'd much rather see explicit
lock/unlock calls used to minimize the critical region rather than having
every exit from a scope automatically releasing one or more locks.

While I tend to avoid nesting locks, there are many cases where they
make sense and for deadlock avoidance the order of acquisition and release
becomes key; something that RAII may not be suitable to control.

Scott Lurndal

unread,
Feb 6, 2023, 8:35:00 PM2/6/23
to
"Alf P. Steinbach" <alf.p.s...@gmail.com> writes:
>On 2023-02-06 10:35 PM, Scott Lurndal wrote:
>> "Alf P. Steinbach" <alf.p.s...@gmail.com> writes:
>>> On 2023-02-05 10:39 PM, Alf P. Steinbach wrote:
>>>> On 2023-02-05 9:49 PM, Chris M. Thomasson wrote:
>
>That is, a `Mutex_lock` is the combination of a lock and a mutex, as the
>name implies.
>
>But now that you mention it, I realize the standard's terminology is not
>really clear. It should be a "locker", not a "lock". Yes.

>> Where is the mutex actually acquired? It is released in the
>> destructor - does the undefined "Mutex_lock" class acquire the
>> mutex in some misguided attempt to apply RAII to locking?
>
>Yes it's RAII, and `std::unique_lock` is the standard library's RAII
>based wrapper for a mutex lock: it acquires a lock in its constructor.
>
>I wouldn't call the standard library's threading support misguided.

Threading and synchronization are cooperative capabilities. I was
specifically refering to using RAII for synchronization - while I was
brought up on pthreads, I certainly don't consider the standard
library's threading support 'misguided', nor did I say it was.

>
>Not because I know enough about the technical to evaluate it, but
>because I know that it's designed by some of the finest & best, and it
>worked fine for me.

I spent a decade on standards committees; compromise is a key
to completing a standard rather than technical excellence (or elegance).

>
>
>> IME, a critical region generally protects more just queue accesses;
>> it may also protect other data or make a certain code sequence atomic
>> with respect to other threads. That's one of the many reasons that
>> I dislike the application of RAII to locking.
>>
>> Whatever agent you used to post this wrapped many of the longer lines
>
>The agent is Mozilla Thunderbird.
>
>It uses a line continuation scheme that's specified in RFC 3676 section
>4.1, of putting a space at the end of a line as continuation symbol;
><url: https://www.ietf.org/rfc/rfc3676.txt>.

Which, as you point out, is not supported by a thirty-plus year old
news reading client like xrn. Personally, I prefer to see what I
wrote, rather than what someone somewhere thinks it should look like.

Someday I'll take the time to update it for UTF-8 and MIME support, multi-thread
it (a chore when using X11 libraries, the interactions with the display server
need to be isolated to a single thread), and add a local header cache.

Someday.

James Kuyper

unread,
Feb 7, 2023, 12:20:13 AM2/7/23
to
On 2/6/23 16:35, Scott Lurndal wrote:
...
> I don't recall 'not', 'and' and 'or' being C++ keywords.

Table 1 in the C++ standard lists alternatives for many tokens:
<% {
%> }
<: [
:> ]
%: #
%:%: ##
and &&
bitor |
or ||
xor ^
compl ~
bitand &
and_eq &=
or_eq |=
xor_eq ^=
not !
not_eq !=

These go way back - "The Design and Evolution of C++" describes them. My
copy of "The C++ Programming Language" and the first version of the C++
standard have unfortunately both gone missing, so I can't confirm
whether they were introduced by that standard, or earlier.

Keith Thompson

unread,
Feb 7, 2023, 12:41:47 AM2/7/23
to
They're in the 1998 ISO C++ standard (2.5 [lex.digraph]).

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for XCOM Labs
void Void(void) { Void(); } /* The recursive call of the void */

Paavo Helde

unread,
Feb 7, 2023, 1:55:54 AM2/7/23
to
For that scenario there is std::scoped_lock (C++17) - "deadlock-avoiding
RAII wrapper for multiple mutexes".

It calls the std::lock() function which "Locks the given Lockable
objects lock1, lock2, ..., lockn using a deadlock avoidance algorithm to
avoid deadlock."

These quotes are from https://en.cppreference.com

It looks like that at least the committee thinks RAII and nested locks
are not contradictory.

Tim Rentsch

unread,
Feb 7, 2023, 10:42:34 AM2/7/23
to
"Alf P. Steinbach" <alf.p.s...@gmail.com> writes:

> On 2023-02-06 10:35 PM, Scott Lurndal wrote:

[...]

>> Whatever agent you used to post this wrapped many of the longer lines
>
> The agent is Mozilla Thunderbird.
>
> It uses a line continuation scheme that's specified in RFC 3676
> section 4.1, of putting a space at the end of a line as continuation
> symbol; <url: https://www.ietf.org/rfc/rfc3676.txt>.
>
> That's called a "soft line break", and the scheme is called "flowed
> format", which you can see in the posting's headers.
>
> Thus, looking at my posting in TB's usual view it's fine, but the raw
> posted text lines are wrapped.

Unfortunately some or many of those trailing spaces don't
survive getting to the client newsreader in the first place.
I've looked carefully. In many cases where the lines are
wrapped, the trailing spaces aren't there.

> Evidently your newsreader XRN (X news reader) or perhaps your version
> of that newsreader, doesn't support RFC 3676's flowed format.

I've looked at the raw input, before it ever gets processed by
the newsreader. On most of the wrapped lines, no trailing spaces.

Alf P. Steinbach

unread,
Feb 7, 2023, 11:34:16 AM2/7/23
to
That's not what I see; furthermore without trailing spaces the articles
would not be formatted correctly in Thunderbird's default view.

It may be a problem with the tool you use to inspect spaces
(highlighting the text by selecting it is an easy way to see spaces), or
it may be your newsreader.

I would guess that like Scott's XRN the Gnus 5.11, from 2007, may not
support modern flowed format. There is however a later version, 5.14
first released in 2013. But its a "development version", whatever that
means. <url: https://en.wikipedia.org/wiki/Gnus>

- Alf

Scott Lurndal

unread,
Feb 7, 2023, 11:36:14 AM2/7/23
to
XRN doesn't "process" the message, it simply displays it as is.

Chris M. Thomasson

unread,
Feb 7, 2023, 3:11:59 PM2/7/23
to
Iirc, it uses a retry and backoff algorithm. There is another way using
address based hash locks where the locks are sorted and duplicates are
removed. It always preserves a strict locking order. A thread can take
several locks without fear of deadlock:

https://groups.google.com/g/comp.lang.c++/c/sV4WC_cBb9Q/m/Ti8LFyH4CgAJ

Ben Bacarisse

unread,
Feb 8, 2023, 11:49:50 AM2/8/23
to
James Kuyper <james...@alumni.caltech.edu> writes:

> On 2/6/23 16:35, Scott Lurndal wrote:
> ...
>> I don't recall 'not', 'and' and 'or' being C++ keywords.
>
> Table 1 in the C++ standard lists alternatives for many tokens:
> <% {
> %> }
> <: [
> :> ]
> %: #
> %:%: ##
> and &&
> bitor |
> or ||
> xor ^
> compl ~
> bitand &
> and_eq &=
> or_eq |=
> xor_eq ^=
> not !
> not_eq !=
>
> These go way back - "The Design and Evolution of C++" describes them. My
> copy of "The C++ Programming Language" and the first version of the C++
> standard have unfortunately both gone missing,

I have the second edition of that book (1991) and they are not
mentioned. I don't recall when they came along but, as Keith as said,
there were there by the first C++ ISO standard (1998).

The names are a bit confusing because not_eq and and_eq share an ending
but one is about equality and the other about assignment.

--
Ben.

red floyd

unread,
Feb 8, 2023, 2:11:16 PM2/8/23
to
On 2/8/2023 8:49 AM, Ben Bacarisse wrote:

> The names are a bit confusing because not_eq and and_eq share an ending
> but one is about equality and the other about assignment.
>

I think they chose those expansions because they match how people would
say those operators when reading code out loud.

Ben Bacarisse

unread,
Feb 8, 2023, 7:41:08 PM2/8/23
to
Yes, I imagine that's the motivation (with "equals" abbreviated). But I
don't read = as "equals". I read it as "is" or "becomes" depending on
whether it's in an initialisation or an assignment, so and_eq sounds all
wrong to me. It's "bitwise and becomes" when I have to read it out.

And it's odd that &&= and ||= don't get alternatives. OK, much less
frequently used, so I suppose that's why.

--
Ben.

Keith Thompson

unread,
Feb 8, 2023, 8:16:07 PM2/8/23
to
I suggest that they're rarely used because they don't exist. (Perl does
have them, but C++ does not.)

Ben Bacarisse

unread,
Feb 8, 2023, 10:07:38 PM2/8/23
to
Keith Thompson <Keith.S.T...@gmail.com> writes:

> Ben Bacarisse <ben.u...@bsb.me.uk> writes:
>> red floyd <no.spa...@its.invalid> writes:
>>> On 2/8/2023 8:49 AM, Ben Bacarisse wrote:
>>>> The names are a bit confusing because not_eq and and_eq share an ending
>>>> but one is about equality and the other about assignment.
>>>
>>> I think they chose those expansions because they match how people would
>>> say those operators when reading code out loud.
>>
>> Yes, I imagine that's the motivation (with "equals" abbreviated). But I
>> don't read = as "equals". I read it as "is" or "becomes" depending on
>> whether it's in an initialisation or an assignment, so and_eq sounds all
>> wrong to me. It's "bitwise and becomes" when I have to read it out.
>>
>> And it's odd that &&= and ||= don't get alternatives. OK, much less
>> frequently used, so I suppose that's why.
>
> I suggest that they're rarely used because they don't exist. (Perl does
> have them, but C++ does not.)

Ah! Good spot, thanks. I must have just assumed...

--
Ben.

Alf P. Steinbach

unread,
Feb 8, 2023, 10:54:43 PM2/8/23
to
C++ also lacks a boolean XOR-operator, though it has a bitwise one. Weird.

For the boolean XOR one can use `!=`, but it has ungood precedence. :(

I don't know of any language that has an general either-or operator, one
that lets you write `a $ b $ c` with meaning `!!a + !!b + !!c == 1`.

Oh, and power-of operator.

Power-of / raised to is really sorely needed, and the trick of writing
e.g. `%rt%` (raised to) has problems beyond being misinterpreted as
placement advertisement for a certain news organization.

- Alf

Öö Tiib

unread,
Feb 9, 2023, 2:38:46 AM2/9/23
to
On Thursday, 9 February 2023 at 05:54:43 UTC+2, Alf P. Steinbach wrote:
> C++ also lacks a boolean XOR-operator, though it has a bitwise one. Weird.
>
> For the boolean XOR one can use `!=`, but it has ungood precedence. :(
>
How != has bad precedence? It is among most frequently used
operators. What would be more intuitive precedence for it?

David Brown

unread,
Feb 9, 2023, 3:11:06 AM2/9/23
to
I believe the point was he wanted a ^^ operator bearing the same
relationship to ^ as && bears to &, and || bears to |. His thought was
that != could be used instead. However, != has precedence matching ==,
above the bitwise operators which are in turn above the logical and and
or operators. To be consistent as a ^^ operator, you'd want something
that had a similar precedence to && and ||.


However, it is wrong to think that != could be used instead of ^^. For
one thing, 4 != 2 is true, while bool(4) ^ bool(2) is false, so it would
give the wrong answer. And one of the prime reasons for having && and
|| operators is their short-circuit evaluation, which would make no
sense for an xor operation.

Tim Rentsch

unread,
Feb 22, 2023, 9:29:03 AM2/22/23
to
Surely displaying the message involves some processing,
even if not very much.

Scott Lurndal

unread,
Feb 22, 2023, 10:01:17 AM2/22/23
to
const char *source_pointer = source;
char *dest_pointer = dest;

while (*source != '\0') *dest_pointer++ = *source_pointer++;

I would call that moving the data, not processing the data.

Tim Rentsch

unread,
Feb 24, 2023, 10:51:45 AM2/24/23
to
I think the newsgroup would be better served if, rather than
speculating about how other tools behave when you don't have any
facts, you would simply limit the line lengths of your postings
to 80 characters, as many coding standards and style guidelines
recommend.

Tim Rentsch

unread,
Feb 24, 2023, 11:34:19 AM2/24/23
to
I think it's fair to say that any effort that involves activity
by the processor may be called processing.

Furthermore your earlier message says "displays". The code shown
doesn't do any displaying.

Alf P. Steinbach

unread,
Feb 24, 2023, 12:48:51 PM2/24/23
to
Even better, that you updated to more modern software. :-o

- Alf

Daniel

unread,
Feb 26, 2023, 12:50:38 PM2/26/23
to
I believe the first time that I've taken issue with Tim Rentsch, but,
yes :-)

Daniel

Bonita Montero

unread,
Mar 3, 2023, 1:50:10 PM3/3/23
to
Use this thread-queue:

#pragma once
#include <deque>
#include <mutex>
#include <condition_variable>
#include <utility>
#include <concepts>
#include <list>
#include <deque>
#include "xscope.h"

template<typename Entity, typename Allocator = std::allocator<Entity>>
struct thread_queue
{
thread_queue();
explicit thread_queue( Allocator const &alloc );
thread_queue( thread_queue const & ) = delete;
thread_queue( thread_queue &&other );
thread_queue &operator =( thread_queue const & ) = delete;
thread_queue &operator =( thread_queue &&other );
bool empty() const;
size_t size() const;
void shrink_to_fit();
void clear();
template<typename ... Args>
#if defined(__cpp_concepts)
requires std::is_constructible_v<Entity, Args ...>
#endif
void enque( Args &&... args );
template<typename Producer>
requires requires( Producer producer ) { { producer() } ->
std::same_as<std::pair<bool, Entity>>; }
void enqueue_multiple( Producer &&producer );
template<typename Consumer>
#if defined(__cpp_concepts)
requires requires( Consumer consumer, Entity value ) { { consumer(
std::move( value ) ) } -> std::same_as<bool>; }
#endif
void dequeue_multiple( Consumer &&consumer );
Entity dequeue();
std::deque<Entity, Allocator> deque();
private:
mutable std::mutex m_mtx;
mutable std::condition_variable m_cv;
std::deque<Entity, Allocator> m_queue;
};

template<typename Entity, typename Allocator>
thread_queue<Entity, Allocator>::thread_queue()
{
}

template<typename Entity, typename Allocator>
thread_queue<Entity, Allocator>::thread_queue( Allocator const &alloc ) :
m_queue( alloc )
{
}

template<typename Entity, typename Allocator>
thread_queue<Entity, Allocator>::thread_queue( thread_queue &&other )
{
std::scoped_lock lock( m_mtx, other.m_mtx );
m_queue = move( other.m_queue );
}

template<typename Entity, typename Allocator>
thread_queue<Entity, Allocator> &thread_queue<Entity,
Allocator>::thread_queue::operator =( thread_queue &&other )
{
using namespace std;
lock_guard
ourLock( m_mtx ),
otherLock( other.m_mtx );
m_queue = move( other.m_queue );
return *this;
}

template<typename Entity, typename Allocator>
bool thread_queue<Entity, Allocator>::thread_queue::empty() const
{
std::lock_guard lock( m_mtx );
return m_queue.empty();
}

template<typename Entity, typename Allocator>
size_t thread_queue<Entity, Allocator>::thread_queue::size() const
{
std::lock_guard lock( m_mtx );
return m_queue.size();
}

template<typename Entity, typename Allocator>
void thread_queue<Entity, Allocator>::thread_queue::shrink_to_fit()
{
#if defined(__cpp_concepts)
if constexpr( requires() { { m_queue.shrink_to_fit() }; } )
#endif
{
std::lock_guard lock( m_mtx );
return m_queue.shrink_to_fit();
}
}

template<typename Entity, typename Allocator>
void thread_queue<Entity, Allocator>::thread_queue::clear()
{
std::lock_guard lock( m_mtx );
m_queue.clear();
}

template<typename Entity, typename Allocator>
template<typename ... Args>
#if defined(__cpp_concepts)
requires std::is_constructible_v<Entity, Args ...>
#endif
void thread_queue<Entity, Allocator>::thread_queue::enque( Args &&... args )
{
using namespace std;
unique_lock lock( m_mtx );
m_queue.emplace_front( forward<Args>( args ) ... );
m_cv.notify_one();
}

template<typename Entity, typename Allocator>
Entity thread_queue<Entity, Allocator>::thread_queue::dequeue()
{
using namespace std;
unique_lock lock( m_mtx );
while( m_queue.empty() )
m_cv.wait( lock );
Entity value = move( m_queue.back() );
m_queue.pop_back();
return value;
}

template<typename Entity, typename Allocator>
template<typename Producer>
#if defined(__cpp_concepts)
requires requires( Producer producer ) { { producer() } ->
std::same_as<std::pair<bool, Entity>>; }
#endif
void thread_queue<Entity, Allocator>::enqueue_multiple( Producer
&&producer )
{
std::lock_guard lock( m_mtx );
for( std::pair<bool, Entity> ret; (ret = move( producer() )).first; )
m_queue.emplace_front( move( ret.second ) ),
m_cv.notify_one();
}

template<typename Entity, typename Allocator>
template<typename Consumer>
#if defined(__cpp_concepts)
requires requires( Consumer consumer, Entity value ) { { consumer(
std::move( value ) ) } -> std::same_as<bool>; }
#endif
void thread_queue<Entity, Allocator>::dequeue_multiple( Consumer
&&consumer )
{
using namespace std;
unique_lock lock( m_mtx );
for( ; ; )
{
while( m_queue.empty() )
m_cv.wait( lock );
xscope_exit popBack( [&]() { m_queue.pop_back(); } );
if( !consumer( move( m_queue.back() ) ) )
return;
}
}

template<typename Entity, typename Allocator>
std::deque<Entity, Allocator> thread_queue<Entity, Allocator>::deque()
{
using namespace std;
unique_lock lock( m_mtx );
return move( m_queue );
}

Chris M. Thomasson

unread,
Mar 3, 2023, 3:45:38 PM3/3/23
to
On 3/3/2023 10:51 AM, Bonita Montero wrote:
> Use this thread-queue:
[...]

Not until you model it in something like Relacy. No time right now to
read all of your code and verify it.

Bonita Montero

unread,
Mar 3, 2023, 11:36:56 PM3/3/23
to
Am 03.03.2023 um 21:45 schrieb Chris M. Thomasson:

> Not until you model it in something like Relacy.
> No time right now to read all of your code and verify it.

Sorry, the code is trivial from the MT-perspective.


Mut...@dastardlyhq.com

unread,
Mar 4, 2023, 4:54:09 AM3/4/23
to
On Fri, 3 Mar 2023 19:51:19 +0100
Bonita Montero <Bonita....@gmail.com> wrote:
>Use this thread-queue:

No thanks.

Life's too short to figure out what its doing but a thread queue should be
a couple of lines of code.

Bonita Montero

unread,
Mar 4, 2023, 9:37:15 AM3/4/23
to
Am 04.03.2023 um 10:53 schrieb Mut...@dastardlyhq.com:

> Life's too short to figure out what its doing but a thread queue should be
> a couple of lines of code.

Show me your code then I show your mistakes and your less to
convenient to use code. Except from the used concepts the code
is straitforward.

Mut...@dastardlyhq.com

unread,
Mar 4, 2023, 10:28:17 AM3/4/23
to
#include <unistd.h>

#include <iostream>
#include <thread>
#include <mutex>
#include <deque>
#include <condition_variable>

using namespace std;

mutex mt;
condition_variable cv;

void func(int id)
{
unique_lock<mutex> ul(mt);
cout << "Thread " << id << " waiting.\n";
cv.wait(ul);
cout << "Thread " << id << " started.\n";
}


int main()
{
deque<thread> dq;
for(int i=0;i < 10;++i) dq.push_back(thread(func,i));

sleep(1);
cv.notify_all();
for(auto &thr: dq) thr.join();
return 0;
}

Bonita Montero

unread,
Mar 4, 2023, 11:15:51 AM3/4/23
to
Am 04.03.2023 um 16:28 schrieb Mut...@dastardlyhq.com:

> mutex mt;
> condition_variable cv;
>
> void func(int id)
> {
> unique_lock<mutex> ul(mt);
> cout << "Thread " << id << " waiting.\n";
> cv.wait(ul);
> cout << "Thread " << id << " started.\n";
> }
>
>
> int main()
> {
> deque<thread> dq;
> for(int i=0;i < 10;++i) dq.push_back(thread(func,i));
>
> sleep(1);
> cv.notify_all();
> for(auto &thr: dq) thr.join();
> return 0;
> }

* This isn't a reusable component.
* Why are you using a deque for the threads and not for the items
you enqueue themselfes ? For the threads a vector<> would be more
convenient to use and if you have a reserve() on it before creating
the threads this would be slightly more efficient.
* You're using a wait which can't handle spurious wakeups.
* sleep() doesn't make sense here and it isn't C++; you should use
this_thread::sleep_for() if you do it at all.
* You could use jthreads and strip the .join()s.

That's not professional programming.

Mut...@dastardlyhq.com

unread,
Mar 4, 2023, 11:22:07 AM3/4/23
to
On Sat, 4 Mar 2023 17:17:02 +0100
Bonita Montero <Bonita....@gmail.com> wrote:
>Am 04.03.2023 um 16:28 schrieb Mut...@dastardlyhq.com:
>
>> mutex mt;
>> condition_variable cv;
>>
>> void func(int id)
>> {
>> unique_lock<mutex> ul(mt);
>> cout << "Thread " << id << " waiting.\n";
>> cv.wait(ul);
>> cout << "Thread " << id << " started.\n";
>> }
>>
>>
>> int main()
>> {
>> deque<thread> dq;
>> for(int i=0;i < 10;++i) dq.push_back(thread(func,i));
>>
>> sleep(1);
>> cv.notify_all();
>> for(auto &thr: dq) thr.join();
>> return 0;
>> }
>
>* This isn't a reusable component.

Why not? You can take the core bits out and put them anywhere.

>* Why are you using a deque for the threads and not for the items
> you enqueue themselfes ? For the threads a vector<> would be more

You wanted a queue, you got a queue.

> convenient to use and if you have a reserve() on it before creating
> the threads this would be slightly more efficient.

Wow, really? Thanks for the heads up there.

>* You're using a wait which can't handle spurious wakeups.

Its a simple example.

>* sleep() doesn't make sense here and it isn't C++; you should use

It does make sense, it's a standard unix API call and I don't need to use
sleep_for.

>* You could use jthreads and strip the .join()s.

"Oh look at me, I can quote obscure parts of C++20 to try and look clever!"

>That's not professional programming.

Its perfectly fine as example code and far more intelligable that your
overcomplicated load of show off bollocks.

Bonita Montero

unread,
Mar 4, 2023, 11:37:54 AM3/4/23
to
Am 04.03.2023 um 17:21 schrieb Mut...@dastardlyhq.com:

> Why not? You can take the core bits out and put them anywhere.

A reusable component is sth. like mine where you won't have to deal
with all the details.

>> * Why are you using a deque for the threads and not for the items
>> you enqueue themselfes ? For the threads a vector<> would be more

> You wanted a queue, you got a queue.

Yes, but a queue with data shared among the threads.
A queue of threads doesn't make sense.

>> * You're using a wait which can't handle spurious wakeups.

> Its a simple example.

It doesn't work reliably because of what I said. You might have
spurious wakeups, i.e. wakeups which occur although nothong happened
on the sender side; that's documented C++-behaviour.

> It does make sense, it's a standard unix API call and I don't need to use
> sleep_for.

If you have a producer-consumer-pattern you don't sleep(), even not with
C++-means.

>> * You could use jthreads and strip the .join()s.

> "Oh look at me, I can quote obscure parts of C++20 to try and look clever!"

The language becomes increasingly more convenient to to use with newer
versions; jthread is one aspect of that.

>> That's not professional programming.

> Its perfectly fine as example code and far more intelligable that your
> overcomplicated load of show off bollocks.

Idiot.


Mut...@dastardlyhq.com

unread,
Mar 4, 2023, 11:57:53 AM3/4/23
to
On Sat, 4 Mar 2023 17:39:05 +0100
Bonita Montero <Bonita....@gmail.com> wrote:
>Am 04.03.2023 um 17:21 schrieb Mut...@dastardlyhq.com:
>
>> Why not? You can take the core bits out and put them anywhere.
>
>A reusable component is sth. like mine where you won't have to deal
>with all the details.

sth?

Understanding your overcomplicated code is a detail in itself.

>>> * Why are you using a deque for the threads and not for the items
>>> you enqueue themselfes ? For the threads a vector<> would be more
>
>> You wanted a queue, you got a queue.
>
>Yes, but a queue with data shared among the threads.
>A queue of threads doesn't make sense.

Well fair enough. In which case something like this.

T consumer()
{
mtx.lock();
auto whatever = dq.pop_front();
mtx.unlock();
return whatever;
}

Similar code for popping it in to the q.

>>> * You're using a wait which can't handle spurious wakeups.
>
>> Its a simple example.
>
>It doesn't work reliably because of what I said. You might have
>spurious wakeups, i.e. wakeups which occur although nothong happened
>on the sender side; that's documented C++-behaviour.

So pass wait() a lambda that checks a flag so see if something did happen.

>> It does make sense, it's a standard unix API call and I don't need to use
>> sleep_for.
>
>If you have a producer-consumer-pattern you don't sleep(), even not with
>C++-means.

As I said you muppet, it was example code. The sleep is so you can see
what is going on. Also FYI sleep() only affects the thread its called from
so it simply pauses the main thread until all the threads are set up.

>>> * You could use jthreads and strip the .join()s.
>
>> "Oh look at me, I can quote obscure parts of C++20 to try and look clever!"
>
>The language becomes increasingly more convenient to to use with newer
>versions; jthread is one aspect of that.

It avoids a single call to detach(). A valuable addition to the language. Not.

>> Its perfectly fine as example code and far more intelligable that your
>> overcomplicated load of show off bollocks.
>
>Idiot.

Truth hurt?

Bonita Montero

unread,
Mar 4, 2023, 12:05:35 PM3/4/23
to
Am 04.03.2023 um 17:57 schrieb Mut...@dastardlyhq.com:
> On Sat, 4 Mar 2023 17:39:05 +0100

> Bonita Montero <Bonita....@gmail.com> wrote:

>> A reusable component is sth. like mine where you won't have to deal
>> with all the details.

> Understanding your overcomplicated code is a detail in itself.

Understanding all what a unordered_map is also complicated.
But you only have to understand the the interface.

> Well fair enough. In which case something like this.
>
> T consumer()
> {
> mtx.lock();
> auto whatever = dq.pop_front();
> mtx.unlock();
> return whatever;
> }

This also doesn't work because you don't test if there are items
inside the queue.


>> It doesn't work reliably because of what I said. You might have
>> spurious wakeups, i.e. wakeups which occur although nothong happened
>> on the sender side; that's documented C++-behaviour.

> So pass wait() a lambda that checks a flag so see if something did happen.

F.e..

>> If you have a producer-consumer-pattern you don't sleep(), even not with
>> C++-means.

> As I said you muppet, it was example code. ...

Even for example code you don't need a wait().

>> The language becomes increasingly more convenient to to use with newer
>> versions; jthread is one aspect of that.

> It avoids a single call to detach(). A valuable addition to the language. Not.

If you'd detach returning from main() and ending the program could occur
earlier than any threads would complete. There's no guarantee that your
runtime waits on detached threads.

Bonita Montero

unread,
Mar 4, 2023, 12:09:36 PM3/4/23
to
Am 04.03.2023 um 18:06 schrieb Bonita Montero:

> If you'd detach returning from main() and ending the program could occur
> earlier than any threads would complete. There's no guarantee that your
> runtime waits on detached threads.

F.e. this prints nothing on my Windows- and Linux-PC:

#include <iostream>
#include <thread>

using namespace std;

int main()
{
thread( []() { cout << "hello world" << endl; } ).detach();
}

Mut...@dastardlyhq.com

unread,
Mar 4, 2023, 12:15:10 PM3/4/23
to
On Sat, 4 Mar 2023 18:06:47 +0100
Bonita Montero <Bonita....@gmail.com> wrote:
>Am 04.03.2023 um 17:57 schrieb Mut...@dastardlyhq.com:
>> On Sat, 4 Mar 2023 17:39:05 +0100
>> T consumer()
>> {
>> mtx.lock();
>> auto whatever = dq.pop_front();
>> mtx.unlock();
>> return whatever;
>> }
>
>This also doesn't work because you don't test if there are items
>inside the queue.

Fine, do a check on the same line and make the return value an optional or
a pointer or a pair with a bool flag or 101 other ways of notifying the caller
when there's nothing available.

Either way, its still only a few lines of simple code unlike yours.

>>> It doesn't work reliably because of what I said. You might have
>>> spurious wakeups, i.e. wakeups which occur although nothong happened
>>> on the sender side; that's documented C++-behaviour.
>
>> So pass wait() a lambda that checks a flag so see if something did happen.
>
>F.e..

No idea what that means.

>>> If you have a producer-consumer-pattern you don't sleep(), even not with
>>> C++-means.
>
>> As I said you muppet, it was example code. ...
>
>Even for example code you don't need a wait().

Have you ever used condition variables?

>> It avoids a single call to detach(). A valuable addition to the language.
>Not.
>
>If you'd detach returning from main() and ending the program could occur
>earlier than any threads would complete. There's no guarantee that your
>runtime waits on detached threads.

There is that, but its hardly important. If you main thread exits then you
almost certainly don't care what any detached threads are doing at that point
anyway.

Bonita Montero

unread,
Mar 4, 2023, 12:40:09 PM3/4/23
to
Am 04.03.2023 um 18:14 schrieb Mut...@dastardlyhq.com:

> On Sat, 4 Mar 2023 18:06:47 +0100

>> This also doesn't work because you don't test if there are items
>> inside the queue.

> Fine, do a check ....

You said that you would give an example.
But it doesn't work for a few reasons.

> Have you ever used condition variables?

Check my code, it uses a CV.

>> If you'd detach returning from main() and ending the program could occur
>> earlier than any threads would complete. There's no guarantee that your
>> runtime waits on detached threads.

> There is that, but its hardly important. ...

For your example code it would be.


Bonita Montero

unread,
Mar 4, 2023, 2:06:42 PM3/4/23
to
My code looks like f.e. like this:

thread_queue<string> stringQueue;
...
stringQueue.enqueue( "hello world" );
...
string dequeued( stringQueue.dequeue() );

That's all.

Chris M. Thomasson

unread,
Mar 4, 2023, 6:25:40 PM3/4/23
to
I have seen too many bugs in code that others have labeled trivial
before... Btw, I am busy on another project. :^)

Chris M. Thomasson

unread,
Mar 4, 2023, 6:26:42 PM3/4/23
to
Take the time to model it in a race detector.

Bonita Montero

unread,
Mar 4, 2023, 11:17:33 PM3/4/23
to
Am 05.03.2023 um 00:26 schrieb Chris M. Thomasson:

> Take the time to model it in a race detector.

You are an idiot; no one needs a race-detector for such simple code.

Bonita Montero

unread,
Mar 4, 2023, 11:18:04 PM3/4/23
to
Am 05.03.2023 um 00:25 schrieb Chris M. Thomasson:

> I have seen too many bugs in code that others have labeled
> trivial before... Btw, I am busy on another project. :^)

Idiot. You didn't have a look at my code.

Tim Rentsch

unread,
Mar 5, 2023, 11:38:28 AM3/5/23
to
Daniel <daniel...@gmail.com> writes:
Note that my comment is not about tools but about matters of
visual presentation. The tools I use have no problem dealing
with long lines but what tools do is not the high order bit here.
There are good human factors reasons to limit line length to
somewhere close to 80 characters, and that point is what I am
hoping to convey.

Note also that I am not the only newsgroup member, nor even the
first, to complain about Alf's idiosyncratic style choices.

Tim Rentsch

unread,
Mar 5, 2023, 11:44:07 AM3/5/23
to
"Alf P. Steinbach" <alf.p.s...@gmail.com> writes:

> On 2023-02-24 4:51 PM, Tim Rentsch wrote:

[...]

>> I think the newsgroup would be better served if, rather than
>> speculating about how other tools behave when you don't have any
>> facts, you would simply limit the line lengths of your postings
>> to 80 characters, as many coding standards and style guidelines
>> recommend.
>
> Even better, that you updated to more modern software. :-o

I'm sad to say that, coming from you, this statement doesn't
surprise me. AFAIAA more people complain about problems your
tools cause than there are for the tools used in my postings.

Chris M. Thomasson

unread,
Mar 5, 2023, 4:38:01 PM3/5/23
to
There is nothing wrong with using a race-detector. Model the simple code
in the detector, then move it into your C++ code. There is nothing wrong
with modeling a lock-based and/or lock-free, wait-free synchronization
scheme in a race detector, before, you flesh it out in real code.

Chris M. Thomasson

unread,
Mar 5, 2023, 4:41:30 PM3/5/23
to
I did, but not long enough to verify it as 100% kosher.

Bonita Montero

unread,
Mar 5, 2023, 11:51:04 PM3/5/23
to
Am 05.03.2023 um 22:37 schrieb Chris M. Thomasson:

> There is nothing wrong with using a race-detector. ...
And there's noting wrong to buy a supercomputer to proof that
one plus one is two.

Mut...@dastardlyhq.com

unread,
Mar 6, 2023, 4:18:32 AM3/6/23
to
On Sat, 4 Mar 2023 18:41:19 +0100
Bonita Montero <Bonita....@gmail.com> wrote:
>Am 04.03.2023 um 18:14 schrieb Mut...@dastardlyhq.com:
>
>> On Sat, 4 Mar 2023 18:06:47 +0100
>
>>> This also doesn't work because you don't test if there are items
>>> inside the queue.
>
>> Fine, do a check ....
>
>You said that you would give an example.
>But it doesn't work for a few reasons.

Feel free to list them.

>> Have you ever used condition variables?
>
>Check my code, it uses a CV.

So why don't you understand what wait() does?

>>> If you'd detach returning from main() and ending the program could occur
>>> earlier than any threads would complete. There's no guarantee that your
>>> runtime waits on detached threads.
>
>> There is that, but its hardly important. ...
>
>For your example code it would be.

No, it wouldn't. The whole point of detached threads is you don't care whether
they terminate before the process terminates. If you did you wouldn't make them
detached in the first place! eg: a thread that blinks a cursor.



Bonita Montero

unread,
Mar 6, 2023, 5:17:32 AM3/6/23
to
Am 06.03.2023 um 10:18 schrieb Mut...@dastardlyhq.com:

> Feel free to list them.

I've shown them before, f.e. you're ignoring spurious wakeups.

> So why don't you understand what wait() does?

The wait doesn't make sense here, even with an example.


Mut...@dastardlyhq.com

unread,
Mar 6, 2023, 5:26:18 AM3/6/23
to
On Mon, 6 Mar 2023 11:18:43 +0100
Bonita Montero <Bonita....@gmail.com> wrote:
>Am 06.03.2023 um 10:18 schrieb Mut...@dastardlyhq.com:
>
>> Feel free to list them.
>
>I've shown them before, f.e. you're ignoring spurious wakeups.

And I wrote how to solve that. Is that it?

>> So why don't you understand what wait() does?
>
>The wait doesn't make sense here, even with an example.

You don't understand what wait() does.

Alf P. Steinbach

unread,
Mar 6, 2023, 9:52:08 AM3/6/23
to
Oh, people have failed to inform you that you're a dimwitted dipshit who
presents fantasy as fact about any topic: technical, or people.

So, hereby done.

- Alf

Bonita Montero

unread,
Mar 6, 2023, 10:09:26 AM3/6/23
to
Am 06.03.2023 um 11:26 schrieb Mut...@dastardlyhq.com:


> And I wrote how to solve that. Is that it?

Your code is lousy, even with this improvement,

> You don't understand what wait() does.

I know what wait does. And I also know that a producer-consumer
relationship doesn't need a wait, even with an example.

I've shown how to use sth. my queue-class form outside; thats
more convenient and less error-prone to to all the thins manually
each time. Using reusable components is mostly beneficial.

Bonita Montero

unread,
Mar 6, 2023, 10:10:04 AM3/6/23
to
Am 05.03.2023 um 22:41 schrieb Chris M. Thomasson:

> I did, but not long enough to verify it as 100% kosher.

The parts that are tricky are purely declarative.
The code itself is trivial.

Mut...@dastardlyhq.com

unread,
Mar 6, 2023, 12:07:44 PM3/6/23
to
On Mon, 6 Mar 2023 16:10:32 +0100
Bonita Montero <Bonita....@gmail.com> wrote:
>Am 06.03.2023 um 11:26 schrieb Mut...@dastardlyhq.com:
>
>
>> And I wrote how to solve that. Is that it?
>
>Your code is lousy, even with this improvement,

Pot meet kettle.

>
>> You don't understand what wait() does.
>
>I know what wait does. And I also know that a producer-consumer
>relationship doesn't need a wait, even with an example.

FFS, the wait() was in the thread queue.

>I've shown how to use sth. my queue-class form outside; thats
>more convenient and less error-prone to to all the thins manually
>each time. Using reusable components is mostly beneficial.

Like all your code, its far too complicated for the simple job it has to do.

Chris M. Thomasson

unread,
Mar 6, 2023, 6:29:36 PM3/6/23
to
Huh? I think you missed my point.

Chris M. Thomasson

unread,
Mar 6, 2023, 6:42:24 PM3/6/23
to
On 3/5/2023 8:52 PM, Bonita Montero wrote:
for instance.. Some code that is "trivial" to me, still deserved to be
implemented in Relacy first:

https://pastebin.com/raw/f71480694

;^)

Bonita Montero

unread,
Mar 7, 2023, 12:43:21 AM3/7/23
to
Am 06.03.2023 um 18:07 schrieb Mut...@dastardlyhq.com:

> FFS, the wait() was in the thread queue.

It doesnt make sense at all.

> Like all your code, its far too complicated for the simple job it has to do.

This is a reusable component which is much easier to use
than what you have shown.

Mut...@dastardlyhq.com

unread,
Mar 7, 2023, 4:31:48 AM3/7/23
to
On Tue, 7 Mar 2023 06:44:31 +0100
Bonita Montero <Bonita....@gmail.com> wrote:
>Am 06.03.2023 um 18:07 schrieb Mut...@dastardlyhq.com:
>
>> FFS, the wait() was in the thread queue.
>
>It doesnt make sense at all.

It makes a lot more sense than any example code you'eve ever written. But
if you don't understand how condition variables control threads then fair
enough.

>> Like all your code, its far too complicated for the simple job it has to do.
>
>This is a reusable component which is much easier to use
>than what you have shown.

I can't imagine anyone using it in their code.

Bonita Montero

unread,
Mar 7, 2023, 8:44:29 AM3/7/23
to
Am 07.03.2023 um 10:31 schrieb Mut...@dastardlyhq.com:

> On Tue, 7 Mar 2023 06:44:31 +0100

> Bonita Montero <Bonita....@gmail.com> wrote:

>> It doesnt make sense at all.
>
> It makes a lot more sense than any example code you'eve ever written. But
> if you don't understand how condition variables control threads then fair
> enough.

As far as you can see from my code I know how producer-consumer-patterns
are handled in C++. As far as I can see from your code you don't.

>> This is a reusable component which is much easier to use
>> than what you have shown.

> I can't imagine anyone using it in their code.

You can't say sat because you're too stupid to understand the code.


Mut...@dastardlyhq.com

unread,
Mar 7, 2023, 10:59:20 AM3/7/23
to
Producer-consumer is a simple solved problem. No one would use your dogs
dinner if you paid them.

Bonita Montero

unread,
Mar 7, 2023, 11:36:16 AM3/7/23
to
Am 07.03.2023 um 16:59 schrieb Mut...@dastardlyhq.com:

> Producer-consumer is a simple solved problem.
> No one would use your dogs dinner if you paid them.

The easiest way is to use pre-defined components for that. I've
shown such a component which is much easier to use than handling
this manually. You didn't even understood that this is a reusable
component.

0 new messages