Hi
I am porting to mqx some existing code that requires recursive mutexes. I.e a mutex that can be locked more than once from one thread. As I had noticed previously that mutexes are stated in the mqx docs to be Posix compliant I was anticipating that mqx mutexes were recursive.
I have just tested this and proved that the mqx mutex is in fact not recursive. So is the documentation wrong, or is Posix loosely defined and doesnt state if recursion is supported or not ?
I wonder if it is possible to make a recursive mutex from a basic mqx mutex with an additional counter ? I am probably overlooking a lot of subtleties here ?
Thanks for any thoughts
Geoff
解決済! 解決策の投稿を見る。
Hi Bezem,
you are right, I can confirm this should work. I wanted to say, that without synchronizing you cannot use a counter as a wrapper. Anyway, to put it to the right way, I correct you (not tested):
typedef struct remutex_struct { _mqx_uint counter; MUTEX_STRUCT mutex; } REMUTEX_STRUCT, _PTR_ REMUTEX_STRUCT_PTR;
_mqx_uint _remutex_lock(REMUTEX_STRUCT_PTR remutex_ptr) { KERNEL_DATA_STRUCT_PTR kernel_data; _GET_KERNEL_DATA(kernel_data); _INT_DISABLE(); if ((remutex_ptr->counter == 0) || (kernel_data->ACTIVE_PTR == remutex-ptr->mutex_ptr.OWNER_TD)) { _mutex_lock(&(remutex_ptr->mutex)); } remutex_ptr->counter++; _INT_ENABLE(); return (MQX_OK); }
_mqx_uint _remutex_unlock(REMUTEX_STRUCT_PTR remutex_ptr) { _INT_DISABLE(); remutex_ptr->counter--; if (remutex_ptr->counter == 0) { _mutex_unlock(&(remutex_ptr->mutex)); } _INT_ENABLE(); return (MQX_OK); }
I have also found this thread very helpful and have been using the implementation described. However, I have a question about the interrupts. What happens to the interrupts if a low priority task has the mutex and then a higher priority task comes and tries to grab the mutex but is blocked. In this case, the interrupts will be disabled just before the block and then there will be a context switch back to the lower priority task so they can finish and release the mutex. Will the interrupts be disabled that whole time?
Thanks,
Dawn Kelsch
I realize this thread is pretty old, but I came across it just today when searching for the solution to a recursive mutex for MQX. Disabling interrupts before calling _mutex_lock() is harmless. In fact, the _mutex_lock() function itself disables interrupts before dropping into the task scheduler.
Hi,
according to user manual section 2.1.205 you should get error: "MQX_EDEADLK - Task already has the mutex locked" in case you are locking mutex recursively. If you are not getting this error then you have found the bug.
I am not Linux expert but from quick Google search I got:
Linux has three kinds of mutex. The mutex kind determines what happens if a thread attempts to lock a mutex it already owns in a pthread_mutex_lock:
Fast mutex: While trying to lock the mutex using the pthread_mutex_lock(), the calling thread is suspended forever.
Recursive mutex: pthread_mutex_lock() immediately returns with a success return code.
Error check mutex: pthread_mutex_lock() immediately returns with the error code EDEADLK.
So MQX is implementing Error check mutex mode. To mimic Recursive mutex you can write simple wrapper function checking error code and returning success (MQX_OK) in case MQX_EDEADLK error is detected
PetrL
JuroV is right, you cannot simply convert an EDEADLK into success, since the concept of recursive mutexes is that the mutex remains locked until all recursive calls have freed the mutex. If you convert the EDEADLK into success, the first unlock will unlock the mutex proper, not what you intended.
> You even cannot mimic this with a new internal wrapper counter (or global variable counter)
> as this counter must be synchronized inside critical section of mutex code.
I beg to differ.
What you can do is to add a counter variable to each mutex (i.e. create your own recursive mutex structure containing one MUTEX_STRUCT and one _mqx_uint counter) and implement your lock as follows:
typedef struct remutex_struct { _mqx_uint counter; MUTEX_STRUCT mutex; } REMUTEX_STRUCT, _PTR_ REMUTEX_STRUCT_PTR; _mqx_uint _remutex_lock(REMUTEX_STRUCT_PTR remutex_ptr){ _INT_DISABLE(); if ((remutex_ptr->counter == 0) || (&(remutex_ptr->mutex)->OWNER_TD == td_ptr)) { _mutex_lock(&(remutex_ptr->mutex)); } remutex_ptr->counter++; _INT_ENABLE(); return (MQX_OK);}
Be aware, this is untested, but I hope the principle comes through. In addition, no error checking is done here, that's left as an exercise
Unlock could be done as follows:
_mqx_uint _remutex_unlock(REMUTEX_STRUCT_PTR remutex_ptr){ _INT_DISABLE(); remutex_ptr->counter--; if (remutex_ptr->counter == 0) { _mutex_unlock(&(remutex_ptr->mutex)); } _INT_ENABLE(); return (MQX_OK);}
Again, without error checking, etc.
Since the _INT_ENABLE()/_INT_DISABLE() are recursive (at least in my MQX sources, see DISABLED_LEVEL), this should work IMHO.
HTH,
Johan
Hi Bezem,
you are right, I can confirm this should work. I wanted to say, that without synchronizing you cannot use a counter as a wrapper. Anyway, to put it to the right way, I correct you (not tested):
typedef struct remutex_struct { _mqx_uint counter; MUTEX_STRUCT mutex; } REMUTEX_STRUCT, _PTR_ REMUTEX_STRUCT_PTR;
_mqx_uint _remutex_lock(REMUTEX_STRUCT_PTR remutex_ptr) { KERNEL_DATA_STRUCT_PTR kernel_data; _GET_KERNEL_DATA(kernel_data); _INT_DISABLE(); if ((remutex_ptr->counter == 0) || (kernel_data->ACTIVE_PTR == remutex-ptr->mutex_ptr.OWNER_TD)) { _mutex_lock(&(remutex_ptr->mutex)); } remutex_ptr->counter++; _INT_ENABLE(); return (MQX_OK); }
_mqx_uint _remutex_unlock(REMUTEX_STRUCT_PTR remutex_ptr) { _INT_DISABLE(); remutex_ptr->counter--; if (remutex_ptr->counter == 0) { _mutex_unlock(&(remutex_ptr->mutex)); } _INT_ENABLE(); return (MQX_OK); }
Gentlemen:
This thread has been so very useful for me. Thank you. I have adopted it in my application. While all seems to be working fine, when I "break" into my program using the CodeWarrior debugger and do a MQX->Check for Errors, I see an error "Not Resource Owner" for the task(s) using the mutex. I found no documentation on this error though I found one more thread on the same issue but which was unanswered. Do you have any information?
Regards,
Ganesh Okade
You probably free some piece of memory in other task than you allocated.
To mimic Recursive mutex you can write simple wrapper function checking error code and returning success (MQX_OK) in case MQX_EDEADLK error is detected
That is not right, please don't do that. There is a difference in those 2 kinds of mutexes. You cannot mimic error-checking mutex with recursive mutex as recursive mutex implements internal counter. You even cannot mimic this with a new internal wrapper counter (or global variable counter) as this counter must be synchronized inside critical section of mutex code.
I'm not familiar enough with the original POSIX spec on this, but recursive mutexes can be crafted using a regular (binary) mutex, a counter and a bit of logic.(r-mutex: recursive mutex, u-mutex: underlying mutex)
To lock the r-mutex:
- if the counter is zero, call lock(u-mutex), increment the counter and exit;
- if the counter is > 0, see if the calling task has locked the mutex:
- if yes, increment the counter and exit;
- if not, call lock(u-mutex) which will block, then check that the counter is 0, increment it; and exit;
To unlock, do the reverse, and make sure to disable interrupts for the appropriate code blocks.
I didn't check whether MQX can be made to support this out-of-the-box, since I tend to avoid these constructs for various reasons.
HTH,
Johan
Hi Johan
Thanks for taking time to post your thoughts on this.
Yes, one of the subtleties I was wondering about was do I need to make it atomic , and if so where? Presumably you said disable interrupts to prevent a possible context switch part way through the rercursive mutex function ?
I was thinking I would need to make both the lock() and unlock() atomic, and disable interrupts for both, but interestingly you said only in the unlock(). Have to ponder that one a bit more as it is a bit non intuitive.
Geoff
Sorry to have confused you. No, you'll need the interrupts locked for both the lock and the unlock, to make sure the mutex and the counter keep in sync in both operations.
HTH,
Johan