Re: Recursive mutex that can be waited upon (pthread)



In article <5apyg.433$Ca.311@xxxxxxxxxxxxxxxxxxxxxxxxx>,
David Hopwood <david.nospam.hopwood@xxxxxxxxxxxxxxxx> wrote:

In your example, Program 2 can break if Program 1 performs a wait
inside its critical region. The wait will unlock Program 2's
lock, and then the state of the object can be changed by some
Program 3, while Program 2 was expecting it to stay constant.

This is an interesting aspect; but I think this is no issue. If you wait
on an object, you are releasing the lock to the object, hence of course
the object is free for being changed. Actually most of the time you wait
on an object because you want it to be changed.

E.g.

Thread 1:

LOCK(myObject);
if (getSize(myObject) == 0) {
// Nothing to do for me, I'll sleep till there is data to process
WAIT(myObject);
}
// Process data of myObject
UNLOCK(myObject);


Thread 2:

// New data arrives
LOCK(myObject);
addDataToObject(myObjcet, myData);
// Tell waiting threads, data is ready
WAKE(myObject);
UNLOCK(myObject);

If Thread 1 kept the lock when going to sleep, Thread 2 could not modify
the object and thus Thread 1 would never wake up again.

I'm trying to avoid spin-locks basically. I could of course do something
like this in Thread 1:

LOCK(myObject);
while (getSize(myObject) == 0) {
UNLOCK(myObject);
// Sleep a few MS
LOCK(myObject);
}
// Process data of myObject
UNLOCK(myObject);

This kind of polling would work as well and it would also work with a
non-recursive mutex, but it's pretty ugly.

Basically I'm just imitating Java here (please ignore semantically
mistakes, I haven't used Java in a long time):

Thread 1:

synchronize(myObject) {
if (myObject.size() == 0) {
try {
myObject.wait();
} catch (InterruptedException e) {
// Ignore
}
}
// Process object
}


Thread 2:

synchronize(myObject) {
myObject.addData(data);
myObject.wakeAll();
}

If I understand the POSIX man page correctly - which is not always that
simple... ;-) - the pthread_cond_wait() has two parameters. The
condition variable threads watch for a *change* of some data and the
*mutex* threads use to synchronize access to that data. And when you go
to sleep, the mutex is released by pthread_cond_wait(), probably that
data can be manipulated elsewhere.

I mean, if my app would like to wait without releasing the lock, it
would not wait on this object, but on some other condition and simply
not release the mutex.

The point is that my code example of above with the two threads works
perfectly without a recursive lock, but as soon as these both functions
are called by other functions, that may or may not have already locked
the object, they will possibly deadlock.


Still I see the problem you are describing: Some thread locks object
myObject to avoid it's getting changed, now it calls a method, and the
method locks at the object and sees that it can't do anything with it,
so it goes to sleep and unlocks the object to make sure the object is
changed; which is exactly the opposite of what the method one step up
the callstack was trying to do.

However, here we have logic flaw in the program. If I lock an object so
it is not changed and then call a method that is documented to *wait*
for an object change, how meaningful is that? If I don't want the object
to be changed, why would I call such a method? This is like trying to
avoid that your application blocks and then calling a read on a socket
that is going to block.

Hence the solution here is to let the caller decide if he wants to wait
or not:

void doSomething(myObject, allowWaiting) {
LOCK(myObject);
if (getSize(myObject) == 0) {
if (!allowWaiting) {
UNLOCK(myObject);

return;
}
// Nothing to do for me, I'll sleep till there is data to process
WAIT(myObject);
}
// Process data of myObject
UNLOCK(myObject);
}

This way the calling method can decide if it wants to wait or not if no
data is available and if it locked the object to avoid that it is
changed, it will simply set allowWaiting to false. The question is how
meaningful the call to doSomething is in this context, as if there are
data available, it is processed and thus the object is changed for sure.
.



Relevant Pages

  • Re: Recursive mutex that can be waited upon (pthread)
    ... on an object, you are releasing the lock to the object, hence of course the object is free for being changed. ... Java's mistake, to be clear, is not necessarily the "full release" wait, but the fact that the language has no support at all for invariants and cannot possibly detect any problems. ... And on top of that the problems are difficult for programmers to avoid because they usually stem from using Java as it should be used -- which is to say that the methods you call are opaque and decoupled. ... However, most functions expect that their invariants are "clean" on entry and exit, and "static" while they hold the mutex. ...
    (comp.programming.threads)
  • RE: Do you really "sleep" when blocked on a mutex?
    ... means that if a thread is holding a lock, ... Do you really "sleep" when blocked on a mutex? ... sched_wakeup which eventually calls setrunqueue()? ...
    (freebsd-hackers)
  • Re: How to wait for multiple threads simultaneously?
    ... A 'condition variable' is the userland-equivalent of a sleep channel ... wait queue to be not worth the effort. ... It simply requires that the lock function not ... because a 'POSIX spin lock' would not ...
    (comp.os.linux.development.apps)
  • Re: Locking etc. (Long, boring, redundant, newbie questions)
    ... What is really meant is that depending on the type of mutex a thread is trying to acquire, the thread will either spin or it will sleep waiting for the lock to become available. ...
    (freebsd-hackers)
  • Re: How to do proper locking
    ... >> refcount reads and no refcount writes. ... Yes, you are right, but the problem is, that for most callback systems in the ... worries about having to exit a lock before returning. ... call any function that can sleep, without worrying about exiting any locks. ...
    (freebsd-hackers)