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

Recursive mutexes

60 views
Skip to first unread message

notme

unread,
Mar 23, 2005, 6:07:59 PM3/23/05
to
I'm trying to implement in C++ a recursive mutex class, because the
operating
system doesn't provide one (it only provides ordinary mutexes and critical
sections)
So far, I have done this:

void my_mutex::acquire() {
EnterCriticalSection();
if (m_owner == thread_id) {
++m_count;
}
else {
m_mutex->wait();
m_count = 1;
m_owner = thread_id;
}
ExitCriticalSection();
}

void my_mutex::release() {
EnterCriticalSection();
--m_count;
if (m_count == 0)
m_mutex->signal();
ExitCriticalSection();
}

I guess the variable's names are self-explanatory. Is this implementation
ok? I'd like to hear from the experts here
any comment or advice about corrections, performance changes, etc. Remember
that I can only use
these primitives.

Thanks!


Chris Thomasson

unread,
Mar 23, 2005, 8:22:01 PM3/23/05
to
> I'm trying to implement in C++ a recursive mutex class, because the
> operating
> system doesn't provide one (it only provides ordinary mutexes and critical
> sections)

CRITICAL_SECTION's are recursive.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/entercriticalsection.asp


Mikhail Polatov

unread,
Mar 23, 2005, 9:51:14 PM3/23/05
to
"notme" <a@b.c> writes:

Why do you need a recursive mutex in the first place?
You can always refactor your code not to use them plus you'll get the
faster code.

--
Mikhail Polatov

notme

unread,
Mar 24, 2005, 5:45:13 AM3/24/05
to
"Chris Thomasson" <_no_damn_spam_cristom@_no_damn_spam.net> escribió en el
mensaje news:Ke-dnaV0w7R...@comcast.com...

Not in my system. Why you assume I'm using windows? :-) (In fact, AFAIK,
windows does support
recursive mutexes). No, in my case they're implemented with a semaphore, so
calling it twice in a row
in the same thread would deadlock. This is an embedded system, and the
synchronization support
isn't very good. It's quite minimal. This is why I need to do it by myself.

Thanks.


notme

unread,
Mar 24, 2005, 5:48:33 AM3/24/05
to

"Mikhail Polatov" <mpol...@meta-comm.com> escribió en el mensaje
news:uvf7hv...@meta-comm.com...

> Why do you need a recursive mutex in the first place?
> You can always refactor your code not to use them plus you'll get the
> faster code.

Thanks for your suggestion, but I can't discuss that :-) I was asked to do
it this way. Besides, is for
adding to existing code, which is quite big, so I presume the decision is
based on that. Refactoring
would take a huge effort.
So, could somebody critique/correct my implementation?
Thanks!


Alexander Terekhov

unread,
Mar 24, 2005, 6:04:19 AM3/24/05
to

notme

unread,
Mar 24, 2005, 7:55:59 AM3/24/05
to
> http://groups.google.de/groups?selm=408E48E8.D67F1EA5%40web.de

Thank you Alexander. I looked into this and it seems very similar to what
I did. Your code pointed me to correct to reset the owner after releasing
the
mutex, which I didn't. Only concern is what "tsd flag key" mean?
Specifically
TSD..?
And your comment about "posix safety", does it refer to destroying the mutex
without releasing it previously (in case it were locked)?
This is your quote:
"Note that if you need "posix safety" with respect to
unlock/destroy of this lock, "tsd::set(&key, false);"
shall be done before "lock.release();" (and of course
the underlying nonrecursive lock itself shall be safe
with respect to unlock/destroy)."

Thanks again.


Alexander Terekhov

unread,
Mar 24, 2005, 8:18:24 AM3/24/05
to

notme wrote:
[...]
> TSD..?

Thread-specific data.

> And your comment about "posix safety", does it refer to destroying the mutex
> without releasing it previously (in case it were locked)?

No.

http://lists.boost.org/MailArchives/boost/msg67633.php
http://lists.boost.org/MailArchives/boost/msg08526.php

regards,
alexander.

notme

unread,
Mar 24, 2005, 9:31:22 AM3/24/05
to
>> And your comment about "posix safety", does it refer to destroying the
>> mutex
>> without releasing it previously (in case it were locked)?
>
> No.

Thank you for your kind help. I'll see if I get into this problem.


Chris Thomasson

unread,
Mar 24, 2005, 6:51:14 PM3/24/05
to
> Not in my system. Why you assume I'm using windows? :-)

DOH!

I instantly thought windows because you used EnterCriticalSection. I failed
to see the call to ExitCriticalSection. Windows has
EnterCriticalSection/LeaveCriticalSection API's. Thats what confuused me.


>(In fact, AFAIK, windows does support recursive mutexes).

Yes it does.


> No, in my case they're implemented with a semaphore, so calling it twice
> in a row
> in the same thread would deadlock. This is an embedded system, and the
> synchronization support
> isn't very good. It's quite minimal. This is why I need to do it by
> myself.

Ok, you can do it like this:


void my_mutex::acquire() {


if (m_owner == thread_id) {
++m_count;

return;
}

EnterCriticalSection();
m_owner = thread_id;
}


void my_mutex::release() {
if (m_count) {
--m_count;
if ( m_count ) { return; }
}

m_owner = 0;
ExitCriticalSection();
}


David Schwartz

unread,
Mar 24, 2005, 7:04:27 PM3/24/05
to

"notme" <a@b.c> wrote in message news:3aeb79F...@individual.net...

> void my_mutex::acquire() {
> EnterCriticalSection();
> if (m_owner == thread_id) {
> ++m_count;
> }
> else {
> m_mutex->wait();

Why are you waiting for the mutex while holding the critical section?
How will the thread that holds the mutex release it?

> m_count = 1;
> m_owner = thread_id;
> }
> ExitCriticalSection();
> }

Also, your code won't work unless thread IDs are unique in the sense
that the same thread can't have more than one of them. This is true on most
platforms unless you are using thread handles instead of thread identifiers.

DS


notme

unread,
Mar 26, 2005, 2:19:46 PM3/26/05
to
>> Not in my system. Why you assume I'm using windows? :-)
>
> DOH!
>
> I instantly thought windows because you used EnterCriticalSection. I
> failed to see the call to ExitCriticalSection. Windows has
> EnterCriticalSection/LeaveCriticalSection API's. Thats what confuused me.

No problem :-)

> Ok, you can do it like this:
>
>
> void my_mutex::acquire() {
> if (m_owner == thread_id) {
> ++m_count;
> return;
> }
>
> EnterCriticalSection();
> m_owner = thread_id;
> }

Interesting approach! So it seems this can be done with just one synch
object!?

> void my_mutex::release() {
> if (m_count) {
> --m_count;
> if ( m_count ) { return; }
> }
>
> m_owner = 0;
> ExitCriticalSection();
> }

Any advantages if enforcing the if with "&& m_owner == thread_id" ? In any
case,
it should be expected the code not being ill behaved. Or I could add this
check and
display an error, right?

Thank you very much for your help. Very enlightening.


notme

unread,
Mar 26, 2005, 2:26:55 PM3/26/05
to
>> void my_mutex::acquire() {
>> EnterCriticalSection();
>> if (m_owner == thread_id) {
>> ++m_count;
>> }
>> else {
>> m_mutex->wait();
>
> Why are you waiting for the mutex while holding the critical section?

Because I'm newbie with this :-)

> How will the thread that holds the mutex release it?

Well, after being elegible to run again. I see, that's not good. Any other
thread
trying to acquire it will be blocked in the critical section... well, in
case if it's the
same thread, that shouldn't be a problem, because when it tries to
re-acquire
it's assumed the mutex was hold. And if it's another thread, it will be
blocked
not in the mutex, but in the critical section. Is it too bad?

So the way to go is using Chris' approach I see.

>> m_count = 1;
>> m_owner = thread_id;
>> }
>> ExitCriticalSection();
>> }
>
> Also, your code won't work unless thread IDs are unique in the sense
> that the same thread can't have more than one of them. This is true on
> most platforms unless you are using thread handles instead of thread
> identifiers.

Yes, I'm using id's.

Thanks for the comments.


doug

unread,
Mar 27, 2005, 7:22:56 AM3/27/05
to

"Chris Thomasson" <_no_damn_spam_cristom@_no_damn_comcast.net_spam> wrote in
message news:HMadndFiKdm...@comcast.com...

>> Not in my system. Why you assume I'm using windows? :-)
>
> DOH!
>
> I instantly thought windows because you used EnterCriticalSection. I
> failed to see the call to ExitCriticalSection. Windows has
> EnterCriticalSection/LeaveCriticalSection API's. Thats what confuused me.
>
>
>
>
>>(In fact, AFAIK, windows does support recursive mutexes).
>
> Yes it does.
>
>
>> No, in my case they're implemented with a semaphore, so calling it twice
>> in a row
>> in the same thread would deadlock. This is an embedded system, and the
>> synchronization support
>> isn't very good. It's quite minimal. This is why I need to do it by
>> myself.
>
> Ok, you can do it like this:
>
>
> void my_mutex::acquire() {
> if (m_owner == thread_id) {
> ++m_count;
> return;
> }
This may not be relevant (depending on your OS/hardware) - but the above
will not work on a multi-CPU system. There is no memory barrier op before
the read of m_owner, so a cpu could read an out of date value from its
cache.
Bug would manifest itself as a thread recursively acquiring mutex (i.e. has
not called ExitCriticalSection() - the barrier which 'writes' the value of
m_owner), but on different cpu, seeing an old value of m_owner, skipping the
increment of m_count and (according to your description of the behaviour)
hanging on EnterCriticalSection.

doug

unread,
Mar 27, 2005, 7:34:30 AM3/27/05
to
I think you were very close with your first attempt. Try this:

void my_mutex::acquire() {
EnterCriticalSection();


if (m_owner == thread_id) {
++m_count;
}

else {
do
* {
* ExitCriticalSection();
m_mutex->wait();
* EnterCriticalSection();
* } while (m_count > 0);


m_count = 1;
m_owner = thread_id;
}
ExitCriticalSection();
}

void my_mutex::release() {
EnterCriticalSection();
--m_count;
if (m_count == 0)

* {
* m_owner = 0;
m_mutex->signal();
* }
ExitCriticalSection();
}

The * lines are new. The second set is just the bug fix for resetting
owner, already pointed out by someone in this thread.

The first set will give you what you want and it's multi-cpu safe (provided
your OS is). It also demonstrates the cardinal rule of coding with mutexes,
signal semphores, etc - "test it before acquiring, and test it again
afterwards" - hence the do..while loop, since we have to drop the mutex crit
section around wait().

Doug


Marcin 'Qrczak' Kowalczyk

unread,
Mar 27, 2005, 7:55:19 AM3/27/05
to
"doug" <no...@nowhere.co.uk> writes:

> void my_mutex::acquire() {
> EnterCriticalSection();
> if (m_owner == thread_id) {
> ++m_count;
> }
> else {
> do
> * {
> * ExitCriticalSection();
> m_mutex->wait();
> * EnterCriticalSection();
> * } while (m_count > 0);
> m_count = 1;
> m_owner = thread_id;
> }
> ExitCriticalSection();
> }

It should be while (m_count > 0) {...}, not do {...} while (m_count > 0).

Alternatively m_count should be tested at the beginning, so the common
case of the mutex being free is faster.

--
__("< Marcin Kowalczyk
\__/ qrc...@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

doug

unread,
Mar 27, 2005, 2:46:34 PM3/27/05
to
"Marcin 'Qrczak' Kowalczyk" <qrc...@knm.org.pl> wrote in message
news:87psxl6...@qrnik.zagroda...

> "doug" <no...@nowhere.co.uk> writes:
>
>> void my_mutex::acquire() {
>> EnterCriticalSection();
>> if (m_owner == thread_id) {
>> ++m_count;
>> }
>> else {
>> do
>> * {
>> * ExitCriticalSection();
>> m_mutex->wait();
>> * EnterCriticalSection();
>> * } while (m_count > 0);
>> m_count = 1;
>> m_owner = thread_id;
>> }
>> ExitCriticalSection();
>> }
>
> It should be while (m_count > 0) {...}, not do {...} while (m_count > 0).
>
> Alternatively m_count should be tested at the beginning, so the common
> case of the mutex being free is faster.

Indeed it should!
This is what you get for making it up on the fly on a Sunday morning after a
heavy night out.

doug

unread,
Mar 27, 2005, 5:21:46 PM3/27/05
to
Repost. Dodgy i/net conn.

"Marcin 'Qrczak' Kowalczyk" <qrc...@knm.org.pl> wrote in message
news:87psxl6...@qrnik.zagroda...

> "doug" <no...@nowhere.co.uk> writes:
>
>> void my_mutex::acquire() {
>> EnterCriticalSection();
>> if (m_owner == thread_id) {
>> ++m_count;
>> }
>> else {
>> do
>> * {
>> * ExitCriticalSection();
>> m_mutex->wait();
>> * EnterCriticalSection();
>> * } while (m_count > 0);
>> m_count = 1;
>> m_owner = thread_id;
>> }
>> ExitCriticalSection();
>> }
>
> It should be while (m_count > 0) {...}, not do {...} while (m_count > 0).
>
> Alternatively m_count should be tested at the beginning, so the common
> case of the mutex being free is faster.
>

Indeed it should!


This is what you get for making it up on the fly on a Sunday morning after a
heavy night out.

I broke my own rule - I didn't test *before*!

Chris Thomasson

unread,
Mar 29, 2005, 6:00:32 PM3/29/05
to
> This may not be relevant (depending on your OS/hardware) - but the above
> will not work on a multi-CPU system. There is no memory barrier op before
> the read of m_owner, so a cpu could read an out of date value from its
> cache.

Yes, you are correct. It was a simple sketch. I believe the barriers would
be extra overhead. If the target OS provides fast access to TLS then Alex's
simple solution would be better.


Chris Thomasson

unread,
Mar 30, 2005, 4:42:57 PM3/30/05
to
> Any advantages if enforcing the if with "&& m_owner == thread_id" ? In any
> case,
> it should be expected the code not being ill behaved. Or I could add this
> check and
> display an error, right?

Yes. You can display an error. I use a very simple mutex error checking
algorithm for my AppCore library. It uses TLS so you don't have to worry
about extra memory barriers which can hamper performance. However, you do
have to check the performance of pthread_self(). Generally I try to minimize
calls to pthread_self() by including a pointer to TLS in the synchronization
object's interface. Here is a snippet of code that implements the simple
error checking on a per-thread basis. Its fairly generic so you should be
able to use its logic for your custom locks:


/* recurse bucket */
typedef struct
ac_thread_shared_recurse_
{
void *lock;
ac_intword_t count;

} ac_thread_shared_recurse_t;


/* thread local storage */
struct
ac_thread_
{
...

/* zero out array on init */
ac_thread_shared_recurse_t recurse[MAX_RECURSE_DEPTH];

/* init to -1 */
ac_intword_t recurse_index;

...
};


/*
api call parameters

_this = pointer to TLS
lock = pointer to synchronization object

ac_sys_thread_recurse_whatever
( ac_thread_t *_this,
void *lock );

return values:

0 = don't access sync object, recurse instead
non-zero = need to access sync object
*/


/* You would call this function before _this
trys to acquire the synchronization object */
int ac_sys_thread_recurse_inc
( ac_thread_t *_this,
void *lock )
{
ac_intword_t recurse = _this->recurse_index;

if ( recurse > -1 && _this->recurse[recurse].lock == lock )
{
++_this->recurse[recurse].count;
return 0;
}

return 1;
}


/* You would call this function after _this
has acquired the synchronization object */
void ac_sys_thread_recurse_locked
( ac_thread_t *_this,
void *lock )
{
ac_intword_t recurse = ++_this->recurse_index;
assert( ! _this->recurse[recurse].count );
_this->recurse[recurse].lock = lock;
}


/* You would call this function before _this
releases the synchronization object */
int ac_sys_thread_recurse_dec
( ac_thread_t *_this,
void *lock )
{
ac_intword_t recurse = _this->recurse_index,
count = _this->recurse[recurse].count;

if ( _this->recurse[recurse].lock != lock )
{
assert( _this->recurse[recurse].lock == lock );
return ac_sys_error( AC_ECORRUPTED );
}

if ( count )
{
_this->recurse[recurse].count = count - 1;
return 0;
}

_this->recurse_index = recurse - 1;

return 1;
}


--
http://appcore.home.comcast.net/
(portable lock-free data-structures)


0 new messages