1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - OS -
3   File:     os_mutex.c
4 
5   Copyright 2003-2008 Nintendo.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2008-11-12#$
14   $Rev: 9293 $
15   $Author: yada $
16  *---------------------------------------------------------------------------*/
17 
18 #include <nitro/types.h>
19 #include <nitro.h>
20 
21 void OSi_UnlockMutexCore(OSMutex *mutex, u32 type);
22 
23 void    OSi_EnqueueTail(OSThread *thread, OSMutex *mutex);
24 void    OSi_DequeueItem(OSThread *thread, OSMutex *mutex);
25 OSMutex *OSi_DequeueHead(OSThread *thread);
26 
27 /*---------------------------------------------------------------------------*
28   Name:         OS_InitMutex
29 
30   Description:  initialize mutex
31 
32   Arguments:    mutex       pointer to mutex structure
33                             to be initialized
34 
35   Returns:      None
36  *---------------------------------------------------------------------------*/
OS_InitMutex(OSMutex * mutex)37 void OS_InitMutex(OSMutex *mutex)
38 {
39     SDK_ASSERT(mutex);
40 
41     OS_InitThreadQueue(&mutex->queue);
42     mutex->thread = NULL;
43     OS_SetMutexCount( mutex, 0 );
44     OS_SetMutexType( mutex, OS_MUTEX_TYPE_NONE );
45 }
46 
47 /*---------------------------------------------------------------------------*
48   Name:         OS_LockMutex
49 
50   Description:  lock mutex
51 
52   Arguments:    mutex       pointer to mutex structure
53 
54   Returns:      None
55  *---------------------------------------------------------------------------*/
OS_LockMutex(OSMutex * mutex)56 void OS_LockMutex(OSMutex *mutex)
57 {
58     OSIntrMode e = OS_DisableInterrupts();
59     OSThread *currentThread = OS_GetCurrentThread();
60 
61     while(1)
62     {
63         //---- try lock mutex
64         if ( OS_TryLockMutex(mutex) )
65         {
66             break;
67         }
68 
69         currentThread->mutex = mutex;
70         OS_SleepThread(&mutex->queue);
71         currentThread->mutex = NULL;
72     }
73 
74     (void)OS_RestoreInterrupts(e);
75 }
76 
77 /*---------------------------------------------------------------------------*
78   Name:         OS_UnlockMutex
79 
80   Description:  unlock mutex
81 
82   Arguments:    mutex       pointer to mutex structure
83 
84   Returns:      None
85  *---------------------------------------------------------------------------*/
OS_UnlockMutex(OSMutex * mutex)86 void OS_UnlockMutex(OSMutex *mutex)
87 {
88     OSi_UnlockMutexCore(mutex, OS_MUTEX_TYPE_STD);
89 }
90 
91 /*---------------------------------------------------------------------------*
92   Name:         OSi_UnlockAllMutex
93 
94   Description:  unlocks all the mutexes locked by the thread
95 
96   Arguments:    mutex       pointer to mutex structure
97 
98   Returns:      None.
99  *---------------------------------------------------------------------------*/
OSi_UnlockAllMutex(OSThread * thread)100 void OSi_UnlockAllMutex(OSThread *thread)
101 {
102     OSMutex *mutex;
103 
104     SDK_ASSERT(thread);
105 
106 #ifndef SDK_THREAD_INFINITY
107     while (thread->mutexQueueHead)
108     {
109         mutex = OSi_DequeueHead(thread);
110         SDK_ASSERT(mutex->thread == thread);
111 
112         OS_SetMutexCount( mutex, 0 );
113         mutex->thread = NULL;
114         OS_SetMutexType( mutex, OS_MUTEX_TYPE_NONE );
115         OS_WakeupThread(&(mutex->queue));
116     }
117 #else
118     while (thread->mutexQueue.head)
119     {
120         mutex = OSi_RemoveMutexLinkFromQueue(&thread->mutexQueue);
121         SDK_ASSERT(mutex->thread == thread);
122 
123         OS_SetMutexCount( mutex, 0 );
124         mutex->thread = NULL;
125         OS_SetMutexType( mutex, OS_MUTEX_TYPE_NONE );
126         OS_WakeupThread(&mutex->queue);
127     }
128 #endif
129 }
130 
131 /*---------------------------------------------------------------------------*
132   Name:         OS_TryLockMutex
133 
134   Description:  try to lock mutex
135 
136   Arguments:    mutex       pointer to mutex structure
137 
138   Returns:      True if lock
139  *---------------------------------------------------------------------------*/
OS_TryLockMutex(OSMutex * mutex)140 BOOL OS_TryLockMutex(OSMutex *mutex)
141 {
142     OSIntrMode saved = OS_DisableInterrupts();
143     OSThread *currentThread = OS_GetCurrentThread();
144     BOOL    locked;
145 
146     SDK_ASSERT(mutex);
147 
148     // ---- able to lock mutex
149     if (mutex->thread == NULL)
150     {
151         mutex->thread = currentThread;
152         OS_SetMutexType( mutex, OS_MUTEX_TYPE_STD );
153         OS_IncreaseMutexCount(mutex);
154         OSi_EnqueueTail(currentThread, mutex);
155         locked = TRUE;
156     }
157     // ---- current thread is same with thread locking mutex
158     else if (mutex->thread == currentThread)
159     {
160         OS_IncreaseMutexCount(mutex);
161         locked = TRUE;
162     }
163     // ---- current thread is different from locking mutex
164     else
165     {
166         locked = FALSE;
167     }
168 
169     (void)OS_RestoreInterrupts(saved);
170     return locked;
171 }
172 
173 //================================================================================
174 // Read / Write Lock
175 //================================================================================
176 /*---------------------------------------------------------------------------*
177   Name:         OS_LockMutexR
178 
179   Description:  lock RW mutex as READ access
180 
181   Arguments:    mutex       pointer to RW mutex structure
182 
183   Returns:      None
184  *---------------------------------------------------------------------------*/
OS_LockMutexR(OSMutex * mutex)185 void OS_LockMutexR(OSMutex *mutex)
186 {
187     OSIntrMode e = OS_DisableInterrupts();
188     OSThread *currentThread = OS_GetCurrentThread();
189 
190     while(1)
191     {
192         //---- try lock by READ
193         if ( OS_TryLockMutexR(mutex) )
194         {
195             break;
196         }
197 
198         currentThread->mutex = mutex;
199         OS_SleepThread(&mutex->queue);
200         currentThread->mutex = NULL;
201     }
202 
203     (void)OS_RestoreInterrupts(e);
204 }
205 
206 /*---------------------------------------------------------------------------*
207   Name:         OS_LockMutexW
208 
209   Description:  lock RW mutex as WRITE access
210 
211   Arguments:    mutex       pointer to RW mutex structure
212 
213   Returns:      None
214  *---------------------------------------------------------------------------*/
OS_LockMutexW(OSMutex * mutex)215 void OS_LockMutexW(OSMutex *mutex)
216 {
217     OSIntrMode e = OS_DisableInterrupts();
218     OSThread *currentThread = OS_GetCurrentThread();
219 
220     while(1)
221     {
222         //---- try lock by WRITE
223         if ( OS_TryLockMutexW(mutex) )
224         {
225             break;
226         }
227 
228         currentThread->mutex = mutex;
229         OS_SleepThread(&mutex->queue);
230         currentThread->mutex = NULL;
231     }
232 
233     (void)OS_RestoreInterrupts(e);
234 }
235 
236 /*---------------------------------------------------------------------------*
237   Name:         OS_TryLockMutexR
238 
239   Description:  try to lock RW mutex as READ access
240 
241   Arguments:    mutex       pointer to RW mutex structure
242 
243   Returns:      TRUE if locked
244  *---------------------------------------------------------------------------*/
OS_TryLockMutexR(OSMutex * mutex)245 BOOL OS_TryLockMutexR(OSMutex *mutex)
246 {
247     OSIntrMode e = OS_DisableInterrupts();
248     BOOL locked = FALSE;
249     OSThread * currentThread = OS_GetCurrentThread();
250 
251     switch( OS_GetMutexType(mutex) )
252     {
253     case OS_MUTEX_TYPE_NONE:
254         mutex->thread = currentThread;
255         OS_SetMutexType( mutex, OS_MUTEX_TYPE_R );
256         OS_SetMutexCount( mutex, 1 );
257         OSi_EnqueueTail(currentThread, mutex);
258         locked = TRUE;
259         break;
260 
261     case OS_MUTEX_TYPE_R:
262         OS_IncreaseMutexCount(mutex);
263         locked = TRUE;
264         break;
265 
266     case OS_MUTEX_TYPE_W:
267     default:
268         break;
269     }
270 
271     (void)OS_RestoreInterrupts(e);
272     return locked;
273 }
274 
275 /*---------------------------------------------------------------------------*
276   Name:         OS_TryLockMutexW
277 
278   Description:  try to lock RW mutex as WRITE access
279 
280   Arguments:    mutex       pointer to RW mutex structure
281 
282   Returns:      TRUE if locked
283  *---------------------------------------------------------------------------*/
OS_TryLockMutexW(OSMutex * mutex)284 BOOL OS_TryLockMutexW(OSMutex *mutex)
285 {
286     OSIntrMode e = OS_DisableInterrupts();
287     BOOL locked = FALSE;
288     OSThread * currentThread = OS_GetCurrentThread();
289 
290     switch( OS_GetMutexType(mutex) )
291     {
292     case OS_MUTEX_TYPE_NONE:
293         mutex->thread = currentThread;
294         OS_SetMutexType( mutex, OS_MUTEX_TYPE_W );
295         OS_SetMutexCount( mutex, 1 );
296         OSi_EnqueueTail(currentThread, mutex);
297         locked = TRUE;
298         break;
299 
300     case OS_MUTEX_TYPE_W:
301         if ( mutex->thread == currentThread )
302         {
303             OS_IncreaseMutexCount(mutex);
304             locked = TRUE;
305         }
306         break;
307 
308     case OS_MUTEX_TYPE_R:
309     default:
310         break;
311     }
312 
313     (void)OS_RestoreInterrupts(e);
314     return locked;
315 }
316 
317 /*---------------------------------------------------------------------------*
318   Name:         OSi_UnlockMutexCore
319 
320   Description:  core routine to unlock mutex
321 
322   Arguments:    mutex       pointer to RW mutex structure
323                 type        mutex type. if different from OS_GetMutexType(), error.
324                               OS_MUTEX_TYPE_STD
325                               OS_MUTEX_TYPE_R
326                               OS_MUTEX_TYPE_W
327                               OS_MUTEX_TYPE_NONE (no check)
328 
329   Returns:      None
330  *---------------------------------------------------------------------------*/
OSi_UnlockMutexCore(OSMutex * mutex,u32 type)331 void OSi_UnlockMutexCore(OSMutex *mutex, u32 type )
332 {
333     OSIntrMode e = OS_DisableInterrupts();
334     OSThread *currentThread = OS_GetCurrentThread();
335     BOOL unlocked = FALSE;
336 
337     SDK_ASSERT(mutex);
338 
339     //---- check unlock type
340     if ( type != OS_MUTEX_TYPE_NONE && type != OS_GetMutexType(mutex) )
341     {
342         //OS_TPanic("Illegal unlock mutex\n");
343         OS_TWarning("Illegal unlock mutex");
344         (void)OS_RestoreInterrupts(e);
345         return;
346     }
347 
348     switch( OS_GetMutexType(mutex) )
349     {
350     case OS_MUTEX_TYPE_STD:
351     case OS_MUTEX_TYPE_W:
352         if ( mutex->thread == currentThread )
353         {
354             OS_DecreaseMutexCount(mutex);
355             if ( OS_GetMutexCount(mutex) == 0 )
356             {
357                 unlocked = TRUE;
358             }
359         }
360         break;
361 
362     case OS_MUTEX_TYPE_R:
363         OS_DecreaseMutexCount(mutex);
364         if ( OS_GetMutexCount(mutex) == 0 )
365         {
366             unlocked = TRUE;
367         }
368         break;
369 
370     default:
371         OS_TWarning("Illegal unlock mutex");
372         (void)OS_RestoreInterrupts(e);
373         return;
374         //OS_TPanic("Illegal unlock mutex\n");
375         //break;
376     }
377 
378     //---- unlock mutex
379     if ( unlocked )
380     {
381         OSi_DequeueItem(currentThread, mutex);
382         mutex->thread = NULL;
383         OS_SetMutexType( mutex, OS_MUTEX_TYPE_NONE );
384         OS_WakeupThread(&mutex->queue);
385     }
386 
387     (void)OS_RestoreInterrupts(e);
388 }
389 
390 /*---------------------------------------------------------------------------*
391   Name:         OS_UnlockMutexR
392 
393   Description:  unlock mutex locked as READ access
394 
395   Arguments:    mutex       pointer to mutex structure
396 
397   Returns:      None
398  *---------------------------------------------------------------------------*/
OS_UnlockMutexR(OSMutex * mutex)399 void OS_UnlockMutexR(OSMutex *mutex)
400 {
401     OSi_UnlockMutexCore( mutex, OS_MUTEX_TYPE_R );
402 }
403 
404 /*---------------------------------------------------------------------------*
405   Name:         OS_UnlockMutexW
406 
407   Description:  unlock mutex locked as WRITE access
408 
409   Arguments:    mutex       pointer to mutex structure
410 
411   Returns:      None
412  *---------------------------------------------------------------------------*/
OS_UnlockMutexW(OSMutex * mutex)413 void OS_UnlockMutexW(OSMutex *mutex)
414 {
415     OSi_UnlockMutexCore( mutex, OS_MUTEX_TYPE_W );
416 }
417 
418 /*---------------------------------------------------------------------------*
419   Name:         OS_UnlockMutexRW
420 
421   Description:  unlock mutex locked as READ/WRITE access
422 
423   Arguments:    mutex       pointer to mutex structure
424 
425   Returns:      None
426  *---------------------------------------------------------------------------*/
OS_UnlockMutexRW(OSMutex * mutex)427 void OS_UnlockMutexRW(OSMutex *mutex)
428 {
429     OSi_UnlockMutexCore( mutex, OS_MUTEX_TYPE_NONE );
430 }
431 
432 /*---------------------------------------------------------------------------*
433   Name:         OS_LockMutexFromRToW
434 
435   Description:  Promote mutexR lock to mutexW lock without unlock.
436                 Wait till success.
437 
438   Arguments:    mutex       pointer to mutex structure
439 
440   Returns:      None
441  *---------------------------------------------------------------------------*/
OS_LockMutexFromRToW(OSMutex * mutex)442 void OS_LockMutexFromRToW(OSMutex *mutex)
443 {
444     OSIntrMode e = OS_DisableInterrupts();
445     OSThread *currentThread = OS_GetCurrentThread();
446 
447     while(1)
448     {
449         if ( OS_TryLockMutexFromRToW(mutex) )
450         {
451             break;
452         }
453 
454         currentThread->mutex = mutex;
455         OS_SleepThread(&mutex->queue);
456         currentThread->mutex = NULL;
457     }
458 
459     (void)OS_RestoreInterrupts(e);
460 }
461 
462 /*---------------------------------------------------------------------------*
463   Name:         OS_TryLockMutexFromRToW
464 
465   Description:  Try to promote mutexR lock to mutexW lock without unlock.
466 
467   Arguments:    mutex       pointer to mutex structure
468 
469   Returns:      TRUE if success
470  *---------------------------------------------------------------------------*/
OS_TryLockMutexFromRToW(OSMutex * mutex)471 BOOL OS_TryLockMutexFromRToW(OSMutex *mutex)
472 {
473     OSIntrMode e = OS_DisableInterrupts();
474     BOOL locked = FALSE;
475 
476     //---- change mutex type if no other thread is locked. and mutex is R
477     if ( OS_GetMutexCount(mutex) == 1 && mutex->queue.head == NULL && OS_GetMutexType(mutex) == OS_MUTEX_TYPE_R )
478     {
479         OS_SetMutexType( mutex, OS_MUTEX_TYPE_W );
480         locked = TRUE;
481     }
482 
483     (void)OS_RestoreInterrupts(e);
484     return locked;
485 }
486 
487 /*---------------------------------------------------------------------------*
488   Name:         OS_LockMutexFromWToR
489 
490   Description:  Demote mutexW lock to mutexR lock without unlock.
491                 Wait till success.
492 
493   Arguments:    mutex       pointer to mutex structure
494 
495   Returns:      None
496  *---------------------------------------------------------------------------*/
OS_LockMutexFromWToR(OSMutex * mutex)497 void OS_LockMutexFromWToR(OSMutex *mutex)
498 {
499     OSIntrMode e = OS_DisableInterrupts();
500     OSThread *currentThread = OS_GetCurrentThread();
501 
502     while(1)
503     {
504         if ( OS_TryLockMutexFromWToR(mutex) )
505         {
506             break;
507         }
508 
509         currentThread->mutex = mutex;
510         OS_SleepThread(&mutex->queue);
511         currentThread->mutex = NULL;
512     }
513 
514     (void)OS_RestoreInterrupts(e);
515 }
516 
517 /*---------------------------------------------------------------------------*
518   Name:         OS_TryLockMutexFromWToR
519 
520   Description:  Try to demote mutexW lock to mutexR lock without unlock.
521 
522   Arguments:    mutex       pointer to mutex structure
523 
524   Returns:      TRUE if success
525  *---------------------------------------------------------------------------*/
OS_TryLockMutexFromWToR(OSMutex * mutex)526 BOOL OS_TryLockMutexFromWToR(OSMutex *mutex)
527 {
528     OSIntrMode e = OS_DisableInterrupts();
529     BOOL locked = FALSE;
530 
531     //---- change mutex type if no other thread is locked. and mutex is W
532     if ( OS_GetMutexCount(mutex) == 1 && mutex->queue.head == NULL && OS_GetMutexType(mutex) == OS_MUTEX_TYPE_W )
533     {
534         OS_SetMutexType( mutex, OS_MUTEX_TYPE_R );
535         locked = TRUE;
536     }
537 
538     (void)OS_RestoreInterrupts(e);
539     return locked;
540 }
541 
542 
543 //===========================================================================
544 //     MUTEX QUEUE
545 //===========================================================================
546 /*---------------------------------------------------------------------------*
547   Name:         OSi_EnqueueTail
548 
549   Description:  internal function.
550                 add mutex to thread's mutex list
551 
552   Arguments:    thread      pointer to thread
553                 mutex       pointer to mutex to be add
554 
555   Returns:      None.
556  *---------------------------------------------------------------------------*/
OSi_EnqueueTail(OSThread * thread,OSMutex * mutex)557 void OSi_EnqueueTail(OSThread *thread, OSMutex *mutex)
558 {
559 #ifndef SDK_THREAD_INFINITY
560     OSMutex *prev = thread->mutexQueueTail;
561 
562     SDK_ASSERT(thread && mutex);
563 
564     if (!prev)
565     {
566         thread->mutexQueueHead = mutex;
567     }
568     else
569     {
570         prev->next = mutex;
571     }
572 
573     mutex->prev = prev;
574     mutex->next = NULL;
575     thread->mutexQueueTail = mutex;
576 #else
577     OSMutex *prev = thread->mutexQueue.tail;
578 
579     SDK_ASSERT(thread && mutex);
580 
581     if (!prev)
582     {
583         thread->mutexQueue.head = mutex;
584     }
585     else
586     {
587         prev->link.next = mutex;
588     }
589 
590     mutex->link.prev = prev;
591     mutex->link.next = NULL;
592     thread->mutexQueue.tail = mutex;
593 #endif
594 }
595 
596 /*---------------------------------------------------------------------------*
597   Name:         OSi_DequeueItem
598 
599   Description:  internal function.
600                 remove specified mutex from thread's mutex list
601 
602   Arguments:    thread      pointer to thread
603                 mutex       pointer to mutex to be remove
604 
605   Returns:      None.
606  *---------------------------------------------------------------------------*/
OSi_DequeueItem(OSThread * thread,OSMutex * mutex)607 void OSi_DequeueItem(OSThread *thread, OSMutex *mutex)
608 {
609 #ifndef SDK_THREAD_INFINITY
610     OSMutex *next = mutex->next;
611     OSMutex *prev = mutex->prev;
612 
613     SDK_ASSERT(thread && mutex);
614 
615     if (!next)
616     {
617         thread->mutexQueueTail = prev;
618     }
619     else
620     {
621         next->prev = prev;
622     }
623 
624     if (!prev)
625     {
626         thread->mutexQueueHead = next;
627     }
628     else
629     {
630         prev->next = next;
631     }
632 #else
633     OSMutex *next = mutex->link.next;
634     OSMutex *prev = mutex->link.prev;
635 
636     SDK_ASSERT(thread && mutex);
637 
638     if (!next)
639     {
640         thread->mutexQueue.tail = prev;
641     }
642     else
643     {
644         next->link.prev = prev;
645     }
646 
647     if (!prev)
648     {
649         thread->mutexQueue.head = next;
650     }
651     else
652     {
653         prev->link.next = next;
654     }
655 #endif
656 }
657 
658 /*---------------------------------------------------------------------------*
659   Name:         OSi_DequeueHead
660 
661   Description:  remove top mutex from thread's list, and return mutex
662 
663   Arguments:    thread      pointer to thread
664 
665   Returns:      mutex which listed at top of thread
666  *---------------------------------------------------------------------------*/
OSi_DequeueHead(OSThread * thread)667 OSMutex *OSi_DequeueHead(OSThread *thread)
668 {
669 #ifndef SDK_THREAD_INFINITY
670     OSMutex *mutex = thread->mutexQueueHead;
671     OSMutex *next = mutex->next;
672 
673     SDK_ASSERT(thread);
674 
675     if (!next)
676     {
677         thread->mutexQueueTail = NULL;
678     }
679     else
680     {
681         next->prev = NULL;
682     }
683 
684     thread->mutexQueueHead = next;
685 #else
686     OSMutex *mutex = thread->mutexQueue.head;
687     OSMutex *next = mutex->link.next;
688 
689     SDK_ASSERT(thread);
690 
691     if (!next)
692     {
693         thread->mutexQueue.tail = NULL;
694     }
695     else
696     {
697         next->link.prev = NULL;
698     }
699 
700     thread->mutexQueue.head = next;
701 #endif
702 
703     return mutex;
704 }
705