void *runPA() { produce('A'); signalSemaphore(&siA); signalSemaphore(&siii); while (1) { produce('A'); signalSemaphore(&siii); } }
void *runPB() { waitSemaphore(&siii); produce('B'); signalSemaphore(&siB); signalSemaphore(&siiC); while (1) { waitSemaphore(&siiB); waitSemaphore(&siii); produce('B'); signalSemaphore(&siiC); } }
void *runC() { while (1) { waitSemaphore(&sC); putchar(buf[ri++]); ri %= BUF_SIZE; signalSemaphore(&sP); } }
void produce(char ch) { pthread_mutex_lock(&mutexP); waitSemaphore(&sP); buf[wi++] = ch; wi %= BUF_SIZE; signalSemaphore(&sC); pthread_mutex_unlock(&mutexP); }
// Signal a specified semaphore after the specified milliseconds duration (the argument). // NOTE: NOT ABSOLUTE VALUE! // If the specified time has already passed, then the TimingSemaphore is signalled immediately. Oop* __fastcall Interpreter::primitiveSignalAtTick(CompiledMethod&, unsigned argumentCount) { Oop tickPointer = stackTop(); SMALLINTEGER nDelay; if (ObjectMemoryIsIntegerObject(tickPointer)) nDelay = ObjectMemoryIntegerValueOf(tickPointer); else { OTE* oteArg = reinterpret_cast<OTE*>(tickPointer); return primitiveFailureWith(PrimitiveFailureNonInteger, oteArg); // ticks must be SmallInteger } // To avoid any race conditions against the global timerID value (it is quite // common for the timer to fire, for example, before the timeSetEvent() call // has actually returned in the duration is very short because the timer thread // is operating at a very high priority), we use an interlocked operation UINT outstandingID = InterlockedExchange(reinterpret_cast<SHAREDLONG*>(&timerID), 0); // If outstanding timer now fires, it will do nothing. We'll end up killing something which is already // dead of course, but that should be OK if (outstandingID) { #ifdef OAD TRACESTREAM << "Killing existing timer with id " << outstandingID << endl; #endif UINT kill = ::timeKillEvent(outstandingID); if (kill != TIMERR_NOERROR) trace("Failed to kill timer %u (%d,%d)!\n\r", outstandingID, kill, GetLastError()); } if (nDelay > 0) { // Temporarily handle old image code that passes timer semaphore as an argument if (argumentCount > 1 && (POTE)Pointers.TimingSemaphore == Pointers.Nil) { ObjectMemory::ProtectConstSpace(PAGE_READWRITE); _Pointers.TimingSemaphore = (SemaphoreOTE*)stackValue(1); ObjectMemory::ProtectConstSpace(PAGE_READONLY); } // Clamp the requested delay to the maximum if it is too large. This simplifies the Delay code in the image a little. if (nDelay > SMALLINTEGER(wTimerMax)) { nDelay = wTimerMax; } // Set the timerID to a non-zero value just in case the timer fires before timeSetEvent() returns. // This allows the TimerProc to recognise the timer as valid (it doesn't really care about the // timerID anyway, just that we're interested in it). // N.B. We shouldn't need an interlocked operation here because, assuming no bugs in the Win32 MM // timers, we've killed any outstanding timer, and the timer thread should be dormant timerID = UINT(-1); // -1 is not used as a timer ID. UINT newTimerID = ::timeSetEvent(nDelay, 0, TimeProc, 0, TIME_ONESHOT); if (newTimerID && newTimerID != UINT(-1)) { // Unless timer has already fired, record the timer id so can cancel if necessary _InterlockedCompareExchange(reinterpret_cast<SHAREDLONG*>(&timerID), newTimerID, -1); pop(argumentCount); // No ref. counting required } else { // System refused to set timer for some reason DWORD error = GetLastError(); trace("Oh no, failed to set a timer for %d mS (%d)!\n\r", nDelay, error); return primitiveFailureWithInt(PrimitiveFailureSystemError, error); } } else if (nDelay == 0) { #ifdef _DEBUG TRACESTREAM << "Requested delay " << dec << nDelay << " passed, signalling immediately" << endl; #endif // The request time has already passed, or does not fall within the // available timer resolution (i.e. it will happen too soon), so signal // it immediately // We must adjust stack before signalling, as may change Process (and therefore stack!) pop(argumentCount); // N.B. Signalling may detect a process switch, but does not actually perform it signalSemaphore(Pointers.TimingSemaphore); } // else requested delay was negative - we allow this to clear down the existing timer #ifdef _DEBUG if (newProcessWaiting()) { ASSERT(m_oteNewProcess->m_oteClass == Pointers.ClassProcess); ProcessOTE* activeProcess = scheduler()->m_activeProcess; TRACESTREAM << "signalAtTick: Caused process switch to " << m_oteNewProcess << endl << "\t\tfrom " << activeProcess << endl << "\tasync signals " << m_qAsyncSignals.isEmpty() << ')' << endl; } #endif // Delay could already have fired CheckProcessSwitch(); return primitiveSuccess(0); }