// Callback proc for MM timers // N.B. This routine is called from a separate thread (in Win32), rather than in // interrupt time, but careful coding is still important. The recommended // list of routines is limited to: // EnterCriticalSection ReleaseSemaphore // LeaveCriticalSection SetEvent // timeGetSystemTime timeGetTime // OutputDebugString timeKillEvent // PostMessage timeSetEvent // // "If a Win32 low-level audio callback [we are using mm timers here] shares data // with other code, a Critical Section or similar mutual exclusion mechanism should // be used to protect the integrity of the data". // Access to the asynchronous semaphore array is protected by a critical section // in the asynchronousSignal and CheckProcessSwitch routines. We don't really care // that much about the timerID void CALLBACK Interpreter::TimeProc(UINT uID, UINT /*uMsg*/, DWORD /*dwUser*/, DWORD /*dw1*/, DWORD /*dw2*/) { // Avoid firing a timer which has been cancelled (or is about to be cancelled!) // We use an InterlockedExchange() to set the value to 0 so that the main thread // can recognise that the timer has fired without race conditions if (_InterlockedExchange(reinterpret_cast<SHAREDLONG*>(&timerID), 0) != 0) { // If not previously killed (which is very unlikely except in certain exceptional // circumstances where the timer is killed at the exact moment it is about to fire) // then go ahead and signal the semaphore and the wakeup event // We mustn't access Pointers from an async thread when object memory is compacting // as the Pointer will be wrong GrabAsyncProtect(); SemaphoreOTE* timerSemaphore = Pointers.TimingSemaphore; HARDASSERT(!ObjectMemoryIsIntegerObject(timerSemaphore)); HARDASSERT(!timerSemaphore->isFree()); HARDASSERT(timerSemaphore->m_oteClass == Pointers.ClassSemaphore); // Asynchronously signal the required semaphore asynchronously, which will be detected // in sync. with the dispatching of byte codes, and properly signalled asynchronousSignalNoProtect(timerSemaphore); // Signal the timing Event, in case the idle process has put the VM to sleep SetWakeupEvent(); RelinquishAsyncProtect(); } else // An old timer (which should have been cancelled) has fired trace("Old timer %d fired, current %d\n", uID, timerID); }
// Queue an APC to the main interpreter thread bool Interpreter::QueueAPC(PAPCFUNC pfnAPC, DWORD dwClosure) { HANDLE hMain = MainThreadHandle(); if (hMain && ::QueueUserAPC(pfnAPC, hMain, dwClosure)) { InterlockedIncrement(&m_nAPCsPending); // The async pending flag is modified in a thread safe manner NotifyAsyncPending(); if (!bIsNT) SetWakeupEvent(); return true; } else return false; }
bool CWaveDevice::InternalOpen() //------------------------------ { MPT_TRACE(); if(m_Settings.InputChannels > 0) { return false; } WAVEFORMATEXTENSIBLE wfext; if(!FillWaveFormatExtensible(wfext, m_Settings)) { return false; } WAVEFORMATEX *pwfx = &wfext.Format; UINT nWaveDev = GetDeviceIndex(); nWaveDev = (nWaveDev > 0) ? nWaveDev - 1 : WAVE_MAPPER; m_ThreadWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if(m_ThreadWakeupEvent == INVALID_HANDLE_VALUE) { InternalClose(); return false; } m_Failed = false; m_DriverBugs = 0; m_hWaveOut = NULL; if(waveOutOpen(&m_hWaveOut, nWaveDev, pwfx, (DWORD_PTR)WaveOutCallBack, (DWORD_PTR)this, CALLBACK_FUNCTION | (m_Settings.ExclusiveMode ? WAVE_FORMAT_DIRECT : 0)) != MMSYSERR_NOERROR) { InternalClose(); return false; } if(waveOutPause(m_hWaveOut) != MMSYSERR_NOERROR) { InternalClose(); return false; } m_nWaveBufferSize = Util::Round<int32>(m_Settings.UpdateInterval * pwfx->nAvgBytesPerSec); m_nWaveBufferSize = Util::AlignUp<uint32>(m_nWaveBufferSize, pwfx->nBlockAlign); m_nWaveBufferSize = mpt::clamp(m_nWaveBufferSize, static_cast<uint32>(WAVEOUT_MINBUFFERFRAMECOUNT * pwfx->nBlockAlign), static_cast<uint32>(Util::AlignDown<uint32>(WAVEOUT_MAXBUFFERSIZE, pwfx->nBlockAlign))); std::size_t numBuffers = Util::Round<int32>(m_Settings.Latency * pwfx->nAvgBytesPerSec / m_nWaveBufferSize); numBuffers = mpt::clamp(numBuffers, WAVEOUT_MINBUFFERS, WAVEOUT_MAXBUFFERS); m_nPreparedHeaders = 0; m_WaveBuffers.resize(numBuffers); m_WaveBuffersData.resize(numBuffers); for(std::size_t buf = 0; buf < numBuffers; ++buf) { MemsetZero(m_WaveBuffers[buf]); m_WaveBuffersData[buf].resize(m_nWaveBufferSize); m_WaveBuffers[buf].dwFlags = 0; m_WaveBuffers[buf].lpData = &m_WaveBuffersData[buf][0]; m_WaveBuffers[buf].dwBufferLength = m_nWaveBufferSize; if(waveOutPrepareHeader(m_hWaveOut, &m_WaveBuffers[buf], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { break; } m_WaveBuffers[buf].dwFlags |= WHDR_DONE; m_nPreparedHeaders++; } if(!m_nPreparedHeaders) { InternalClose(); return false; } m_nBuffersPending = 0; m_nWriteBuffer = 0; m_nDoneBuffer = 0; { MPT_LOCK_GUARD<mpt::mutex> guard(m_PositionWraparoundMutex); MemsetZero(m_PositionLast); m_PositionWrappedCount = 0; } SetWakeupEvent(m_ThreadWakeupEvent); SetWakeupInterval(m_nWaveBufferSize * 1.0 / m_Settings.GetBytesPerSecond()); m_Flags.NeedsClippedFloat = GetSysInfo().WindowsVersion.IsAtLeast(mpt::Windows::Version::WinVista); return true; }