+ // Check if we have to block
+ if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
+ {
+ tThread *us;
+ #if 0
+ Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name);
+ Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue);
+ #endif
+
+ SHORTLOCK( &glThreadListLock );
+ // - Remove from active list
+ us = Threads_RemActive();
+ us->Next = NULL;
+ // - Mark as sleeping
+ us->Status = THREAD_STAT_SEMAPHORESLEEP;
+ us->WaitPointer = Sem;
+ us->RetStatus = AmmountToAdd; // Use RetStatus as a temp variable
+
+ // - Add to waiting
+ if(Sem->LastSignaling) {
+ Sem->LastSignaling->Next = us;
+ Sem->LastSignaling = us;
+ }
+ else {
+ Sem->Signaling = us;
+ Sem->LastSignaling = us;
+ }
+
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("%p (%i %s) signaling semaphore %p %s:%s",
+ us, us->TID, us->ThreadName,
+ Sem, Sem->ModName, Sem->Name);
+ #endif
+
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &Sem->Protector );
+ while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
+ // We're only woken when there's something avaliable
+ us->WaitPointer = NULL;
+
+ added = us->RetStatus;
+
+ // Get the lock again
+ SHORTLOCK( &Sem->Protector );
+ }
+ // Non blocking
+ else
+ {
+ // Figure out how much we need to take off
+ if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
+ added = Sem->MaxValue - Sem->Value;
+ else
+ added = AmmountToAdd;
+ Sem->Value += added;
+ }
+
+ // While there are items avaliable, and there are thread waiting
+ // wake the first thread and give it what it wants (or what's left)
+ while( Sem->Value && Sem->Waiting )
+ {
+ tThread *toWake = Sem->Waiting;
+
+ // Remove thread from list (double ended, so clear LastWaiting if needed)
+ Sem->Waiting = Sem->Waiting->Next;
+ if( Sem->Waiting == NULL )
+ Sem->LastWaiting = NULL;
+
+ // Figure out how much to give to woken thread
+ // - Requested count is stored in ->RetStatus
+ if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
+ given = toWake->RetStatus;
+ else
+ given = Sem->Value;
+ Sem->Value -= given;
+
+ // Save the number we gave to the thread's status
+ toWake->RetStatus = given;
+
+ if(toWake->bInstrTrace)
+ Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem);
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("%p (%i %s) woken by signal on %p %s:%s",
+ toWake, toWake->TID, toWake->ThreadName,
+ Sem, Sem->ModName, Sem->Name);
+ #endif
+
+ // Wake the sleeper
+ SHORTLOCK( &glThreadListLock );
+ if( toWake->Status != THREAD_STAT_ACTIVE )
+ Threads_AddActive(toWake);
+ else
+ Warning("Thread %p (%i %s) is already awake", toWake, toWake->TID, toWake->ThreadName);
+ SHORTREL( &glThreadListLock );
+ }
+ SHORTREL( &Sem->Protector );
+
+ return added;