What are the downsides to non-predictable scheduling?
This question is motivated by code Stevens uses in Section 7.5 of his
Network Programming, Vol. 2. The snippet below is part of his final
producer/consume example in Figure 7.7. This code has a race if the
system schedules the "signallee" thread before the "signaller" unlocks
the mutex.
He notes this race condition in his text, but then goes on to say that
with Posix, "if predictable scheduling behavior is required, then the
mutex must be locked by the thread calling pthread_cond_wait."
Pthread_mutex_lock(&nready.mutex);
if (nready.nready == 0)
Pthread_cond_signal(&nready.cond);
nready.nready++;
Pthread_mutex_unlock(&nready.mutex);
Thanks,
m
Whoops, been answered here.
(Searching on "predictable scheduling" was much better search foo.)
Thanks,
m
Is the last sentence a direct quote from the book? The quote as-is seems to
suggest that there are times in which you can call 'pthread_cond_wait()'
with an unlocked mutex. That's troubling because you cannot ever call
'pthread_cond_wait()' with a pointer to an unlocked mutex...
> Is the last sentence a direct quote from the book? The quote as-is seems to
> suggest that there are times in which you can call 'pthread_cond_wait()'
> with an unlocked mutex. That's troubling because you cannot ever call
> 'pthread_cond_wait()' with a pointer to an unlocked mutex...
The last sentence is obviously wrong, however, not only his quoted
sentence but your last sentence as well. In fact, you _can_ call a
`pthread_cond_wait()' with a pointer to an unlocked mutex any time.
You should not, but you can. The compiler will not tell you anything.
Neither will you get any error code at run time. Just check it out.
Well, do not forget that C is not a multi-threaded language and
Pthread is just a library. Thus, there is no compiler support for
multi-threading issues in C.
I must stress that calling the `pthread_cond_wait()' with an unlocked
mutex is a definite bug but the guys who `collectively spent hundreds
of years (sic) on the pthread issues' somehow missed to check the
obvious things.
Yeah, multicore programming is even harder without proper language
support.
Best Regards,
Szabolcs
> > Is the last sentence a direct quote from the book? The quote as-is seems
> > to
> > suggest that there are times in which you can call 'pthread_cond_wait()'
> > with an unlocked mutex. That's troubling because you cannot ever call
> > 'pthread_cond_wait()' with a pointer to an unlocked mutex...
> The last sentence is obviously wrong, however, not only his quoted
> sentence but your last sentence as well. In fact, you _can_ call a
> `pthread_cond_wait()' with a pointer to an unlocked mutex any time.
> You should not, but you can. The compiler will not tell you anything.
> Neither will you get any error code at run time. Just check it out.
Of course the compiler will not tell you anything.
> Well, do not forget that C is not a multi-threaded language and
> Pthread is just a library. Thus, there is no compiler support for
> multi-threading issues in C.
PThread is NOT just a library. POSIX Threads puts restrictions on a C
compiler.
> I must stress that calling the `pthread_cond_wait()' with an unlocked
> mutex is a definite bug but the guys who `collectively spent hundreds
> of years (sic) on the pthread issues' somehow missed to check the
> obvious things.
Are you serious?
> Yeah, multicore programming is even harder without proper language
> support.
Speak for yourself.
POSIX defines a system-wide standard:
http://groups.google.com/group/comp.programming.threads/msg/729f412608a8570d
> POSIX defines a system-wide standard:
>
> http://groups.google.com/group/comp.programming.threads/msg/729f41260...
Library standard but not any language one. A big difference! That is
why there is no compiler support for it. A compiler can support the
language only.
I hope I could help.
Best Regards,
Szabolcs
> > POSIX defines a system-wide standard:
> >
> > http://groups.google.com/group/comp.programming.threads/msg/729f41260...
> Library standard but not any language one. A big difference!
The C language does not need any more operators or keywords in order to
support POSIX threads. Also, I don't think that the upcomming C++ standard
adds any new keywords or operators to support threading.
> That is
> why there is no compiler support for it. A compiler can support the
> language only.
The compiler needs to render code that will not breaking a correct
multi-threaded program. So, the POSIX standard does have an effect on the
way a C compiler actually compiles the language. It should not perform
certain optimizations in critical-sections for instance. Check this out:
http://groups.google.com/group/comp.programming.threads/browse_frm/thread/63f6360d939612b3
It does not need any more operators or keywords if it stays to be a
sequential language. On the contrary, if you want to make secure multi-
threaded programs, you need a language that supports it. A library is
just not enough. It is not enough because on the one hand only a
language can support the thinking process and, on the other hand,
compilers cannot support library element combinations. It is because
library calls are not part of the language.
> Also, I don't think that the upcomming C++ standard
> adds any new keywords or operators to support threading.
Yes, probably not. It remains a sequential programming language. So
simple it is.
> > That is
> > why there is no compiler support for it. A compiler can support the
> > language only.
>
> The compiler needs to render code that will not breaking a correct
> multi-threaded program. So, the POSIX standard does have an effect on the
> way a C compiler actually compiles the language.
Yes, the "C compiler actually compiles the language". The language
stays to be sequential. So the compiler supports a sequential
language. Is it not clear to you?
> It should not perform
> certain optimizations in critical-sections for instance. Check this out:
>
> http://groups.google.com/group/comp.programming.threads/browse_frm/th...
Critical section is simply not known to the compiler. In C, there is
no language means to express that you want to make a piece of code a
critical section. All you have are pthread library calls. That is why,
if you would finally try it out, you can put the `pthread_cond_wait()'
outside the _intended_ critical section. The compiler is not aware of
the misuse!
That is the point. Once upon a time there were language proposals to
incorporate the notion of the critical section into language level.
Until it is not the case, the compiler cannot check the proper use of
the critical section.
I hope I was of help, though.
Best Regards,
Szabolcs
Chris' sentence is correct. "Can" in this context obviously means
"can correctly". There are lots of things you "can" do in C which
are semantic errors yet the language allows it. Your nitpick, while
technically correct, doesn't add anything.
> Well, do not forget that C is not a multi-threaded language and
> Pthread is just a library. Thus, there is no compiler support for
> multi-threading issues in C.
>
> I must stress that calling the `pthread_cond_wait()' with an unlocked
> mutex is a definite bug but the guys who `collectively spent hundreds
> of years (sic) on the pthread issues' somehow missed to check the
> obvious things.
I don't see the problem (within the limitations of the language).
Within C there is no way to enforce this. Even if pthread_cond_wait()
were required to use a locked mutex (beyond the existing semantic
requirement), no guarantee can be made that it is the correct mutex.
I guess they could have defined the cv to be a struct with 2 members
(the cv itself and an associated mutex) but that assumes you will
always use the mutex only along with the cv.
You are correct, issues such as these do point out the deficiencies
in using a language like C with threads.
-frank
OK, so you're nitpicking grammar. (And given your writing, this is
rather "bold" of you.) So, yes, shall we clarify instead that one "can"
but "must not". It violates the standard; it is a serious application
error. When one steps off a cliff, nature does not raise an error flag
and put you back safely on the ground; that doesn't mean it was a good
idea. (Nor, for that matter, that physics is not well supported by nature.)
> Well, do not forget that C is not a multi-threaded language and
> Pthread is just a library. Thus, there is no compiler support for
> multi-threading issues in C.
As Chris said, Pthreads is far more than "a library", because it cannot
run on top of or as part of a "generic ISO C" or "traditional BSD/SVR
UNIX" -- it places additional stringent requirements for thread-safety
on compiler, runtime, and OS.
Still, you are correct in the implication that there are constraints and
limitations placed by the structure of the language beyond the aspects
of ISO C that POSIX could extend or "profile". Some things (e.g.,
portable lock-free/wait-free constructs) could be enabled only by
language changes.
> I must stress that calling the `pthread_cond_wait()' with an unlocked
> mutex is a definite bug but the guys who `collectively spent hundreds
> of years (sic) on the pthread issues' somehow missed to check the
> obvious things.
"Pthreads" is not an implementation, or even an ABI. It's a source-level
API standard. And, no, we very definitely did not "missed[sic] to check
the obvious things". There is a specifically defined error code for
pthread_cond_wait() to report this condition IF an implementation
chooses to spend the time to detect it. (Unfortunately, POSIX/UNIX error
granularity is not exactly "overly expressive", so it's one of several
possible interpretations of EINVAL.)
Those who wanted to be sure Pthreads supported the greatest possible
runtime performance did not want the standard to be bogged down with
overhead for handholding bad programs. So detection of any error that is
strictly an avoidable application bug was intended to be optional.
> Yeah, multicore programming is even harder without proper language
> support.
Yeah, just like most anything worthwhile or useful is hard. Learning to
speak a language, read music, play piano, design user interfaces...
It's not about being easy, it's about mastering the skills.
> It does not need any more operators or keywords if it stays to be a
> sequential language. On the contrary, if you want to make secure multi-
> threaded programs, you need a language that supports it.
You can create standard, secure and correct multi-threaded programs with
PThreads and C; is that not clear to you?
[...]
> > Also, I don't think that the upcomming C++ standard
> > adds any new keywords or operators to support threading.
> Yes, probably not. It remains a sequential programming language. So
> simple it is.
Luckily, C++ is flexible enough to handle threading without changes in
syntax.
> > > That is
> > > why there is no compiler support for it. A compiler can support the
> > > language only.
> >
> > The compiler needs to render code that will not breaking a correct
> > multi-threaded program. So, the POSIX standard does have an effect on
> > the
> > way a C compiler actually compiles the language.
> Yes, the "C compiler actually compiles the language". The language
> stays to be sequential. So the compiler supports a sequential
> language. Is it not clear to you?
Of course.
> > It should not perform
> > certain optimizations in critical-sections for instance. Check this out:
> >
> > http://groups.google.com/group/comp.programming.threads/browse_frm/th...
> Critical section is simply not known to the compiler. In C, there is
> no language means to express that you want to make a piece of code a
> critical section. All you have are pthread library calls. That is why,
> if you would finally try it out, you can put the `pthread_cond_wait()'
> outside the _intended_ critical section. The compiler is not aware of
> the misuse!
> That is the point. Once upon a time there were language proposals to
> incorporate the notion of the critical section into language level.
> Until it is not the case, the compiler cannot check the proper use of
> the critical section.
If C is too low-level for your programming abilities, then use something
else. Try Haskell or something...
I would say that neither the Stevens quote nor Chris' response is
"wrong" - once the relevant context in each case has been
established.
Chris's statement - that a program "cannot" call pthread_cond_wait()
with an unlocked mutex - was clearly (if implicitly) limited to the
set of all correct programs; After all, no one is interested in
writing a program that is not correct. So Chris's statement is right:
a correct program cannot call pthread_cond_wait() with an unlocked
mutex.
For the Stevens quote, the rest of the book establishes the needed
context.Stevens assumes that the reader has in fact read more of his
book than the one sentence quoted. In particular, Stevens assumes that
the reader will remember that ptrhread_cond_wait's mutex must be
locked. So, in light of that requirement, the only possible
interpretation of the quoted sentence left to the reader is, simply,
that the locked mutex passed to pthread_cond_wait() must have been
locked by the [same] thread that calls pthread_cond_wait() [and not by
some other thread].
In other words, the issue in the Stevens quote is not whether the
mutex is locked or not - but whether the thread that locked the mutex
is the same thread that calls pthread_cond_wait().
> I must stress that calling the `pthread_cond_wait()' with an unlocked
> mutex is a definite bug but the guys who `collectively spent hundreds
> of years (sic) on the pthread issues' somehow missed to check the
> obvious things.
The statement may be "obviously" wrong, but yet at the same time, it
is subtly correct.
Greg
Second edition 14th printing says pthread_cond_signal not wait.
Well ... let's not get overly defensive.
While what you say is (of cousr) true, the fact remains that it IS
difficult and there are other language choices that can make it
easier. First class support for threading is, in general, an
advantage, just like first class support for objects can be an
advantage.
-frank
Ahh, okay, that makes a more sense.
;^)
> While what you say is (of cousr) true, the fact remains that it IS
> difficult and there are other language choices that can make it
> easier. First class support for threading is, in general, an
> advantage, just like first class support for objects can be an
> advantage.
Agreed. I just personally like the low-level nature of C, and can't wait to
implement something under the upcoming C++ Standard. I think that
standardized support for creating non-blocking algorithms is going to be a
very good thing indeed. BTW, have you checked out Intel's C++ STM compiler?
It is trying to introduce new language constructs (e.g., transactional
atomic blocks) that deal with concurrency...
Well, the compiler does not agree with you in that "Chris' sentence is
correct." It is very simple. Give it a try. No bias but test. You can
call a 'pthread_cond_wait()' with an unlocked mutex and the compiler
will not object against it.
C is a very low level language, hence it is popular. In sequential
programming you can allow yourself the luxury to develop bugs into
your program in a low level language such as C because it will be just
a matter of time to de-bug it. Do you know why? Because your
sequential program is deterministic. You can use a debugger to get rid
of the bug you have just put into your program.
However, you will not be able to debug a concurrent program the same
way. It is simply because a concurrent program is non-deterministic.
Consequently, if you want to make secure multi-threaded programs, you
will need the support of the compiler to avoid as many bugs as
possible already at compile time. It may seem to be a nitpick to you
guys until you gain some experience with concurrent programs.
How many examples can we see copied into this discussion forum where
you see shared variables used outside critical region. Beginners and
even experienced programmers can easily make this mistake without
proper language support.
> > Well, do not forget that C is not a multi-threaded language and
> > Pthread is just a library. Thus, there is no compiler support for
> > multi-threading issues in C.
>
> > I must stress that calling the `pthread_cond_wait()' with an unlocked
> > mutex is a definite bug but the guys who `collectively spent hundreds
> > of years (sic) on the pthread issues' somehow missed to check the
> > obvious things.
>
> I don't see the problem (within the limitations of the language).
> Within C there is no way to enforce this. Even if pthread_cond_wait()
> were required to use a locked mutex (beyond the existing semantic
> requirement), no guarantee can be made that it is the correct mutex.
Well, may be you do not see the problem but it does not mean that the
problem is not there. The condition variable is required to use a
locked mutex and it must be ensured that it is the correct mutex. It
is the question of proper notation at the language level. Of course,
if you are at the library level, it is difficult, but that is not the
right way to get into concurrent programming. You simply need a proper
language for it.
> I guess they could have defined the cv to be a struct with 2 members
> (the cv itself and an associated mutex) but that assumes you will
> always use the mutex only along with the cv.
It is the other way around. You need the mutex only because you want
to build a Critical Region by hand since the Critical Region is not
part of the language. Furthermore, you need the condition variable
only to make your hand built Conditional Critical Region efficient.
If you read about where the condition variable originates, you will be
surprised that it is not the invention of POSIX. There has been life
in concurrent programmig before POSIX too. Actually, the condition
variable is just an optimisation element for avoiding frequent looping
in a busy waiting loop.
Consider the simple Critical Region first. Remember that the mutex is
just a binary semaphore and with any binary semaphore you can
implement a Critical Region. I will use some ad hoc notation. So, if C
would be extended with a keyword to mark a critical resource (e.g.
`shared') and another keyword for the critical region (e.g. `region'),
you would not need the mutex for it. E.g.
shared struct {
int n, k;
} r = {0, 0};
region r { n++; k++; }
This example declares a shared resource and shows a code fragment to
atomically set new values to it. You do not need any mutex because the
compiler can generate it for you as appropriate. The compiler can also
check it for you that the resource `r' should not be accessed outside
the critical region.---If your language misses something like that,
you will need binary semaphores (note that the mutex is a binary
semaphore) to implement the same, although you will miss the support
of the compiler, e.g.
volatile int n, k;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&m);
n++;
k++;
pthread_mutex_unlock(&m);
If you do not have binary semaphore but you have counting semaphore,
you can still build a critical region like this:
volatile int n, k;
sem_t m;
enum {CLOSED_SEMA=0, OPEN_SEMA=1};
enum {THREAD_SHARED=0};
sem_init(&m, THREAD_SHARED, OPEN_SEMA);
sem_wait(&m);
n++;
k++;
sem_post(&m);
Remember, in the case you built the critical region by hand you also
missed the support of the compiler.
You will need the condition variable when you want to move further and
need a Conditional Critical Region built by hand. An example in the
original language proposal for the Conditional Critical Region could
be expressed in a provisorical C-like notation like this (keywords
`par' and `also' build a structured parallel statement, `await' is a
keyword to denote waiting for a condition while releasing the
resource):
shared struct {
int n, k;
} r = {0, 0};
assert(n==0);
assert(k==0);
par {
region r {
k++;
n++;
}
} also {
region r {
await(n==1 && k==1);
k=n=2;
}
}
assert(n==2);
assert(k==2);
In this example, one process increments the two variables and the
other awaits a certain state of the variables. This could be built by
hand with help of the mutex like this (note that there is no condition
variable used yet):
struct {
volatile int n, k;
pthread_mutex_t m;
} r = {0, 0, PTHREAD_MUTEX_INITIALIZER};
void *tt(void *a)
{
pthread_mutex_lock(&r.m);
while (!(r.n==1 && r.k==1)) {
pthread_mutex_unlock(&r.m);
pthread_mutex_lock(&r.m);
}
r.k=r.n=2;
pthread_mutex_unlock(&r.m);
return NULL;
}
int main()
{
pthread_t th;
pthread_create(&th, NULL, tt, NULL);
pthread_mutex_lock(&r.m);
r.k++;
r.n++;
pthread_mutex_unlock(&r.m);
pthread_join(th, NULL);
assert(n==2);
assert(k==2);
return 0;
}
As you can see, a busy loop is used (n `tt') to wait for the condition
(not for the condition variable). If there were plenty of processing
elements available and each and every process would be performed by a
dedicated processor, there would be no problem with the busy waiting
loop. The processor power would not be used for anything else
anyway.---In real systems one processor always multiplexes the
processing power between several processes, so if one process performs
a busy wait like this, it means waste of resources.
Needless to say that you can build the same construction shown above
from counting semaphores instead of mutexes.
This is the point where in the history of concurrent programming the
secure queueing variables have been proposed later known as condition
variables. It is used to optimise the busy loop like this:
struct {
volatile int n, k;
pthread_mutex_t m;
pthread_cond_t c;
} r = {0, 0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
void *tt(void *a)
{
pthread_mutex_lock(&r.m);
while (!(r.n==1 && r.k==1)) {
pthread_cond_wait(&r.c, &r.m);
}
r.k=r.n=2;
pthread_mutex_unlock(&r.m);
return NULL;
}
int main()
{
pthread_t th;
pthread_create(&th, NULL, tt, NULL);
pthread_mutex_lock(&r.m);
r.k++;
r.n++;
pthread_cond_signal(&r.c);
pthread_mutex_unlock(&r.m);
pthread_join(th, NULL);
assert(n==2);
assert(k==2);
return 0;
}
Now, the waiting process goes to sleep and it is waken up only if
there can be some change in the shared data space, thus the awaited
condition can be re-evaluated. So, the busy waiting is optimised.
However, if you have the Conditional Critical Region at language
level, the compiler can insert the necessary mutexes and condition
variables for you as needed. You do not have to bother with low level
issues like this.
> You are correct, issues such as these do point out the deficiencies
> in using a language like C with threads.
I hope I was of help.
Best Regards,
Szabolcs
You are conveniently ignoring the accusation of misguided pedantry.
Everyone, including myself, has agreed with you. Yet you are wrong.
> C is a very low level language, hence it is popular.
I don't see how that follows. Assembly language is even more low level,
yet it is not popular.
> In sequential programming you can allow yourself the luxury to
> develop bugs into your program in a low level language such as C
> because it will be just a matter of time to de-bug it. Do you know
> why? Because your sequential program is deterministic. You can use a
> debugger to get rid of the bug you have just put into your program.
I'd disagree with that, but that's a different argument.
> However, you will not be able to debug a concurrent program the same
> way. It is simply because a concurrent program is non-deterministic.
> Consequently, if you want to make secure multi-threaded programs, you
> will need the support of the compiler to avoid as many bugs as
> possible already at compile time. It may seem to be a nitpick to you
> guys until you gain some experience with concurrent programs.
>
> How many examples can we see copied into this discussion forum where
> you see shared variables used outside critical region. Beginners and
> even experienced programmers can easily make this mistake without
> proper language support.
Again you won't find anyone here that doesn't agree. C and C++ are
difficult and especially so for concurrent programming. (Yet they
are still valuable.)
>> I don't see the problem (within the limitations of the language).
>> Within C there is no way to enforce this. Even if pthread_cond_wait()
>> were required to use a locked mutex (beyond the existing semantic
>> requirement), no guarantee can be made that it is the correct mutex.
>
> Well, may be you do not see the problem but it does not mean that the
> problem is not there.
Again you insist on conveniently ignoring part of a response in order
to further your own agenda here. I have already agreed with you, even
in the paragraph you are challenging, that C is not ideal for
concurrent programming.
> Remember that the mutex is just a binary semaphore
Not within POSIX. A mutex makes specific memory visibility guarantees
that a binary semaphore does not.
> shared struct {
> int n, k;
> } r = {0, 0};
>
> region r { n++; k++; }
...
Your thoughts along these lines are well known by everyone here, and
valid. You are wasting your time.
-frank
> Well, the compiler does not agree with you in that "Chris' sentence is
> correct." It is very simple. Give it a try. No bias but test. You can
> call a 'pthread_cond_wait()' with an unlocked mutex and the compiler
> will not object against it.
Do you want a compiler to lead the blind? You are the programmer which
passes an unlocked mutex to 'pthread_cond_wait()' and complains that the
compiler allows you to do so... Welcome to programming! You want mommy to
hold your hand? Get real!
I advise you to use a language with STM.
> C is a very low level language, hence it is popular. In sequential
> programming you can allow yourself the luxury to develop bugs into
> your program in a low level language such as C because it will be just
> a matter of time to de-bug it. Do you know why? Because your
> sequential program is deterministic. You can use a debugger to get rid
> of the bug you have just put into your program.
> However, you will not be able to debug a concurrent program the same
> way.
[...]
Why do you think that debugging a single-threaded sequential program should
be equal to debugging a concurrent one? AFAICT, you don't know how to use a
C/C++ debugger in a multi-threaded environment. Please read all of the
following links!
http://groups.google.com/group/comp.programming.threads/msg/c5004dea42e42aaa
(a debug session that targets a lock-free algorithm...)
http://groups.google.com/group/comp.programming.threads/msg/a0212ab012519a8c
http://groups.google.com/group/comp.programming.threads/msg/83c533c0a3dbfc0a
Your assertion that you will not be able to debug a concurrent program in C
is completely false! In the first link I posted, well, read it for yourself.
I can recreate any scenario you can think of in the debugger. I have
experience in that area. Don't try any mislead anybody here with your bogus
assertion that you will not be able to debug a concurrent program the same
> volatile int n, k;
> pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
> pthread_mutex_lock(&m);
> n++;
> k++;
> pthread_mutex_unlock(&m);
> If you do not have binary semaphore but you have counting semaphore,
> you can still build a critical region like this:
[...]
Why are you using volatile when your code suggests that its a POSIX program?
That tells me something about your multi-threading abilities... Wow... Even
in Windows, you don't need volatile for code confined within a
critical-section. There is so much you wrote that is erroneous, well, I
don't have time right now to type out ten paragraphs... Well, from your
language proposals, why don't you just use a lang with STM built in?
Haskell? Intel C++ STM experiment?'
?
I will respond in full at a latter date...
> I must stress that calling the `pthread_cond_wait()' with an unlocked
> mutex is a definite bug
Definitely.
> but the guys who `collectively spent hundreds
> of years (sic) on the pthread issues' somehow missed to check the
> obvious things.
Huh? Who are you saying missed this? If you find some code that has
this bug, point us to it and we'll publicly flog whoever wrote it.
DS
Nitpicking or not, I said something similar (predicting reactions like
yours), see: "You should not, but you can." Just read it a couple of
lines above. Nevertheless, there are some guys here for whom you
should clarify this, I am afraid.
> It violates the standard; it is a serious application
> error. When one steps off a cliff, nature does not raise an error flag
> and put you back safely on the ground; that doesn't mean it was a good
> idea. (Nor, for that matter, that physics is not well supported by nature.)
>
When one steps off a cliff, nature _does rise_ an error flag. Any
healthy adult can read that error message and obeys it. If you fail to
recognise that error flag or you feel free to disobey it, well, the
program will be fixed and your thread will be terminated.
We know that "God does not play dice with the universe" but we can
also add that nature does not write sequential or deterministic
programs. Rather, mother nature runs evolutionary programs. Thus, if
you fail to recognise the error flag or try to ignore it, you will
have no chance to propagate your erroneous genes, hence the bug is
fixed. So simple is that.
Best Regards,
Szabolcs
I believe he is referring to the sheer idiocy of not having a method in the
library to check the arguments passed for obvious errors. Not that this
checking needs to be on all the time.
Read the documentation, an implementation can return EINVAL is so chooses.
> Consider the simple Critical Region first. Remember that the mutex is
> just a binary semaphore and with any binary semaphore you can
> implement a Critical Region.
How many mutex implementations have you looked at? Are you sure that they
are based on bin-sema?
> I will use some ad hoc notation. So, if C
> would be extended with a keyword to mark a critical resource (e.g.
> `shared') and another keyword for the critical region (e.g. `region'),
> you would not need the mutex for it. E.g.
> shared struct {
> int n, k;
> } r = {0, 0};
> region r { n++; k++; }
> This example declares a shared resource and shows a code fragment to
> atomically set new values to it. You do not need any mutex because the
> compiler can generate it for you as appropriate.
[...]
How could I use custom locking strategy with this? Let's say I want to use a
custom distributed rw-mutex:
http://groups.google.com/group/comp.programming.threads/msg/1731cf0404e3d400
http://groups.google.com/group/comp.programming.threads/browse_frm/thread/a23aeb712e8dbdf9
what keyword would you dream up for that one? I need a low-level language
that won't try and generate mutexs or any of that non-sense. C as-is fits
the bill. Your keyword proposals are already implemented in languages that
support STM in the form of atomic blocks.
Yeah, the error is in the form of a toe-tag at the morgue... Ouch!
> Any healthy adult can read that error message and obeys it.
What are you talking about? You said: "> When one steps off a cliff, nature
_does rise_ an error flag."... I have to point out that when you step off a
cliff, its too late to read an error message.
> If you fail to
> recognise that error flag or you feel free to disobey it, well, the
> program will be fixed and your thread will be terminated.
How does terminating a thread fix your program?
> We know that "God does not play dice with the universe"
The quantum level is all about non-deterministic behavior.
> but we can
> also add that nature does not write sequential or deterministic
> programs. Rather, mother nature runs evolutionary programs.
Evolution is non-deterministic in nature.
> Thus, if
> you fail to recognise the error flag or try to ignore it, you will
> have no chance to propagate your erroneous genes, hence the bug is
> fixed. So simple is that.
Oh boy... Are you off your meds?
Whoops. I misunderstood you when you said that a mutex is just a bin-sema. I
think you meant that the end-result of a mutex is analogous to a binary
semaphore.
OK. Now I see that those ancient men hundreds of years ago did not
miss it but really implemented it in an unpredictable way.
Let us consider this simple test:
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
int main()
{
if ((errno=pthread_cond_wait(&c, &m))) {
perror("error flag");
exit(EXIT_FAILURE);
}
return 0;
}
I have checked it with Linux and found this (no compiler warning or
error, no run time error):
$ gcc -v
...
Thread model: posix
gcc version 3.2.3 20030502 (Red Hat Linux 3.2.3-54)
$ gcc -Wall -W condvar_problem.c -lpthread
$ ./a.out
##### HANGING HERE
$
I have checked in with MinGW as well (no compiler warning or error but
at least there is a run time error):
$ gcc -v
...
Thread model: win32
gcc version 3.4.2 (mingw-special)
$ gcc -Wall -W condvar_problem.c -lpthread
$ ./a.exe
error flag: Invalid argument
$
So, it is unpredictable indeed.
Best Regards,
Szabolcs
> OK. Now I see that those ancient men hundreds of years ago did not
> miss it but really implemented it in an unpredictable way.
Ancient men hundreds of years ago? WTF? Anyway, read the documentation where
it very clearly says that 'pthread_cond_timed/wait()' MAY fail with EINVAL
for a variety of reasons, one of those being a mutex which is not owned by
the calling thread. It's completely up to the implementation to do this. The
POSIX standard was very wise not to force this "nanny-like" behavior. I
believe that your implementations did not enforce the MAY clause in this
particular case because its too much overhead just to protect incompetent
programmers from shooting themselves.
> Let us consider this simple test:
[...]
The implementation MAY decide to help you, but many of them will not bother
to return EINVAL in this case. So far, this test only proves that you don't
know what you are doing. You cannot place blame on the implementation for
not detecting every error you make. It's not the impl's fault.
I suggest you read the documentation. There is no way to require an
implementation to return an error, when you want e.g. debugging phase. That is
the problem.
Hmm... Debug build of library?
Dmitriy V'jukov
The Standard says an implementation MAY return EINVAL for a number of
reasons; read here:
http://www.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
See?
(e.g., Linux)
> did not enforce the MAY clause in this particular case because its too
> much overhead just to protect incompetent programmers from shooting
> themselves.
[...]
> I suggest you read the documentation. There is no way to require an
> implementation to return an error, when you want e.g. debugging phase. That is
> the problem.
There is not required to be a portable way, but debugging never is.
DS
> Let us consider this simple test:
[snip]
> So, it is unpredictable indeed.
So what? It's trivial to write code that's unpredictable.
DS
You have confused the word MAY with the word MUST. Next you'll come back with
something about nanny's or the like. No, conditionally and for the debugging
phase. But unless the standard has a requirement to check, implementations are
free to allow detectable bugs to persist until they become a real problem and
for people other than those who own, use or are ever aware of the system.
A robot may not injure a human being or, through inaction, allow a human being
to come to harm. - Isaac Asimov
The MAY clause means that its not required and therefore an implementation
can choose to support it.
> Next you'll come back with something about nanny's or the like.
:^)
> No, conditionally and for the debugging phase. But unless the standard
> has a requirement to check, implementations are free to allow detectable
> bugs to persist until they become a real problem and for people other than
> those who own, use or are ever aware of the system.
[...]
I guess an implementer can simply provide debug and retail versions of their
pthread library. However, I still would not like the standard to force them
to provide error codes for every little thing...
We have a difference of philosophy on this ...
Mr. Thomasson, I understand you selected the implementation involved it this case?
You were aware that the library was not required to report detectable errors and
in fact the standard specifically says it may not return errors when they exist
and could have been detected?
Are you familiar with the Three Laws of Robotics postulated by Isaac Asimov?
Can you explain to the jury what is meant by, "A robot may not injure a human
being or, THROUGH INACTION, allow a human being to come to harm."
Can you explain to the jury the part about inaction with respect to a library
not returning error codes for errors it could have checked for?
Mr. Thomasson, is it not true that if the implementation had returned an error
code the system would have gone into safe mode?
Mr. Thomasson, isn't it your choice of the implementation the reason Mr. Jones
is dead?
I don't want to be answering these types of questions in a court room and people
who design things for use in the real world don't want to either. Even if the
question isn't about someone being dead but simply why their bank account is
empty and their identity stolen.
> We have a difference of philosophy on this ...
Yes, you think that defensive engineering doesn't have costs that can
outweigh its benefits.
> Can you explain to the jury the part about inaction with respect to a library
> not returning error codes for errors it could have checked for?
It is a sensible engineering trade-off that sensible engineers make.
If you don't think it's sensible (and it isn't always) you don't have
to make it.
> Mr. Thomasson, is it not true that if the implementation had returned an error
> code the system would have gone into safe mode?
We're by definition talking about cases the implementor couldn't have
designed for. If he had specifically addressed this case, he would
have simply fixed it rather than devising a better work around.
Suppose the "safe mode" causes the system to fall into inaction and
kill someone whereas ignoring the error would have permitted normal
operation to continue?
> Mr. Thomasson, isn't it your choice of the implementation the reason Mr. Jones
> is dead?
Yes, but it's the reason Mr. Smith is alive.
> I don't want to be answering these types of questions in a court room and people
> who design things for use in the real world don't want to either. Even if the
> question isn't about someone being dead but simply why their bank account is
> empty and their identity stolen.
Then don't become an engineer or programmer. We have to make these
kinds of tradeoffs all the time. Sometimes we have to guess about
what's the right thing to do in situations we can't at all foresee
(and would have avoided if we had) and sometimes we guess wrong.
DS
This is total fallacy. If "Mr Jones" died because you applied an
undebugged program to his life support, it's not the fault of the
Pthread implementation.
Yeah, sure, if the implementation reported that usage error you'd have
gotten a failure. What are the chances such a stupid coding error would
have occurred in isolation? What are the chances such a lazy programmer
would even bother to check the return values?
You can't rely on an implementation to detect all possible application
bugs -- many of the worst are beyond the scope of the API and any
possible checks it might apply.
It's the programmer's responsibility to ensure the application is
suitable for the purpose; especially true for funds transfer, or life
support.
Indeed, implementation debugging capabilities are one of many TOOLS the
programmer might use to help debug and test the application first; but
ultimately it's the programmer's responsibility to understand and
properly apply the tools available. If such trivially stupid coding
errors aren't caught by the implementation you're using, there are
plenty of other tools you can use during development.
And if the bug suddenly crops up during real production use of the
program, these API detection points are way too late to save Mr Jones,
or your money. We're talking about SYMPTOMS of an application bug with
probably much worse consequences already in progress. Again, only the
application developer can categorize, detect, and prevent such bugs
early enough to do any real good.
In any case, such detection is no substitute for thorough testing; and
the testing is the application developer's responsibility, not the
system implementor's.
> I don't want to be answering these types of questions in a court room
> and people who design things for use in the real world don't want to
> either. Even if the question isn't about someone being dead but simply
> why their bank account is empty and their identity stolen.
Got news for you, buddy. While there ARE some types of system failures
that might involve the library or system provider, this isn't one of
them. That's YOU up on the stand testifying why you didn't bother to
test and debug your critical application before applying it to a live
patient (or funds transfer system)... because you hoped that the system
might, through sheer blind luck, have detected your bug before it was
too late and forced the buggy application to fail in a harmless way
despite having no knowledge at all of the problem domain.
In other words, you're arguing that it wasn't YOUR responsibility to
design or write an application suitable for the advertised purpose. That
someone else should have done it for you.
Good luck with that.
I am afraid it is not about any difference in philosophy but rather it
is about the mythology of an engineering issue. Even the mind of some
people is the result of an engineering trade-off as the following
story tells us: When God has created the different characters for the
people, after some time he has run out of pudency and goodwill but
there was plenty of arrogance and hybris left over. So, as an
engineering trade-off, he used them to fill the remaining space in the
abilities compartment in some buddies. Now they are the ones who can
find and present any obviously false excuse with enormous confidence
for their careless work.
Now back to the technical issues: Despite of the furious reaction
explaining why it is not a good idea to check error situations, we
know that it could have been easily implemented with a little care
about safeness. Even if it is only a library, it can be checked (and
any professional engineer would indeed check it) if a function is
called with arguments which do not fulfill the precondition. Since the
library level lacks any language support, it would be especially
important.
After all, the pthread library provides the so-called
pthread_condattr_t data structure which could have a flag just for
this requirement. Then, if you decide that it is important for you to
check the error codes of a particular condition variable, you just set
up the attribute accordingly so that the error codes must be
predictably returned. There would be no performance penalty whatsoever
but some more safety would be back. However, if some buddy just wants
to step off the cliff, he is free to do so---very efficiently, of
course.
I have checked the structure of the attribute pthread_condattr_t in a
Linux implementation. It looks like it is the result of an engineering
trade-off as well:
/* Attribute for conditionally variables. */
typedef struct
{
int __dummy;
} pthread_condattr_t;
Plenty of room for improvements.
Besides, instead of the library level, safety can be rather achieved
at the language level. Unfortunately the programming languages today
are also careless in this respect, they do not provide too much
language level support for multi-threading. They only provide
something comparable to the pthread library. Java and the likes
provide the `synchronized' keyword but despite they fail to implement
a simple critical region correctly, since the (intentionally) shared
variables can appear outside the synchronized blocks.
The bad news is that people even do not realize that they would need
language support as it is proven in this discussion thread too.
They just want freedom to step off the cliff.
Best Regards,
Szabolcs
What about this:
Q: Mr. Automaker, I understand you create cars with steering wheels, is that
correct?
------
A: Yes Sir.
Q: Did your so-called steering wheel allow the now deceased Mr. Smith to
turn into and drive right off a cliff?
------
A: Yes Sir.
Q: Mr. Automaker, since you make cars with steering wheels, and that device
allowed Mr. Smith to kill himself, the only possible conclusion is that its
all your fault! You careless murderer!
------
A: Bullshi%
LOL! :^)
% Now back to the technical issues: Despite of the furious reaction
% explaining why it is not a good idea to check error situations, we
I haven't noticed any of this.
% know that it could have been easily implemented with a little care
% about safeness.
Well, I just tried it and, in fact, pthreads reported an error. Then it
called abort() just to make sure I got the idea. So I guess that means you
don't have an argument. When you say the error is not checked, you're wrong.
--
Patrick TJ McPhee
North York Canada
pt...@interlog.com