예제 #1
0
s32 sys_semaphore_wait(u32 sem_id, u64 timeout)
{
	sys_semaphore.Log("sys_semaphore_wait(sem_id=%d, timeout=%lld)", sem_id, timeout);

	Semaphore* sem;
	if (!Emu.GetIdManager().GetIDData(sem_id, sem))
	{
		return CELL_ESRCH;
	}

	const u32 tid = GetCurrentPPUThread().GetId();
	const u64 start_time = get_system_time();

	{
		std::lock_guard<std::mutex> lock(sem->m_mutex);
		if (sem->m_value > 0)
		{
			sem->m_value--;
			return CELL_OK;
		}
		sem->m_queue.push(tid);
	}

	while (true)
	{
		if (Emu.IsStopped())
		{
			sys_semaphore.Warning("sys_semaphore_wait(%d) aborted", sem_id);
			return CELL_OK;
		}

		if (timeout && get_system_time() - start_time > timeout)
		{
			sem->m_queue.invalidate(tid);
			return CELL_ETIMEDOUT;
		}

		if (tid == sem->signal)
		{
			std::lock_guard<std::mutex> lock(sem->m_mutex);

			if (tid != sem->signal)
			{
				continue;
			}
			sem->signal = 0;
			// TODO: notify signaler
			return CELL_OK;
		}

		SM_Sleep();
	}
}
예제 #2
0
s32 sys_semaphore_post(u32 sem_id, s32 count)
{
	sys_semaphore.Log("sys_semaphore_post(sem_id=%d, count=%d)", sem_id, count);

	Semaphore* sem;
	if (!Emu.GetIdManager().GetIDData(sem_id, sem))
	{
		return CELL_ESRCH;
	}

	if (count < 0)
	{
		return CELL_EINVAL;
	}

	if (count + sem->m_value - (s32)sem->m_queue.count() > sem->max)
	{
		return CELL_EBUSY;
	}

	while (count > 0)
	{
		if (Emu.IsStopped())
		{
			sys_semaphore.Warning("sys_semaphore_post(%d) aborted", sem_id);
			return CELL_OK;
		}

		std::lock_guard<std::mutex> lock(sem->m_mutex);

		if (sem->signal && sem->m_queue.count())
		{
			SM_Sleep();
			continue;
		}

		if (u32 target = (sem->protocol == SYS_SYNC_FIFO) ? sem->m_queue.pop() : sem->m_queue.pop_prio())
		{
			count--;
			sem->signal = target;
			Emu.GetCPU().NotifyThread(target);
		}
		else
		{
			sem->m_value += count;
			count = 0;
		}
	}

	return CELL_OK;
}
예제 #3
0
void sys_ppu_thread_once(mem_ptr_t<std::atomic<be_t<u32>>> once_ctrl, u32 entry)
{
	sysPrxForUser->Warning("sys_ppu_thread_once(once_ctrl_addr=0x%x, entry=0x%x)", once_ctrl.GetAddr(), entry);

	be_t<u32> old = SYS_PPU_THREAD_ONCE_INIT;
	if (once_ctrl->compare_exchange_weak(old, SYS_PPU_THREAD_DONE_INIT))
	{
		CPUThread& new_thread = Emu.GetCPU().AddThread(CPU_THREAD_PPU);
		new_thread.SetEntry(entry);
		new_thread.Run();
		new_thread.Exec();

		while (new_thread.IsAlive()) SM_Sleep();
	}
}
예제 #4
0
s32 sys_cond_wait(u32 cond_id, u64 timeout)
{
	sys_cond.Log("sys_cond_wait(cond_id=%d, timeout=%lld)", cond_id, timeout);

	Cond* cond;
	if (!Emu.GetIdManager().GetIDData(cond_id, cond))
	{
		return CELL_ESRCH;
	}

	Mutex* mutex = cond->mutex;
	u32 tid = GetCurrentPPUThread().GetId();

	if (mutex->m_mutex.GetOwner() != tid)
	{
		sys_cond.Warning("sys_cond_wait(cond_id=%d) failed (EPERM)", cond_id);
		return CELL_EPERM;
	}

	cond->m_queue.push(tid);

	if (mutex->recursive != 1)
	{
		sys_cond.Warning("sys_cond_wait(cond_id=%d): associated mutex had wrong recursive value (%d)", cond_id, mutex->recursive);
	}
	mutex->recursive = 0;
	mutex->m_mutex.unlock(tid, mutex->protocol == SYS_SYNC_PRIORITY ? mutex->m_queue.pop_prio() : mutex->m_queue.pop());

	u64 counter = 0;
	const u64 max_counter = timeout ? (timeout / 1000) : ~0ull;

	while (true)
	{
		if (cond->signal.unlock(tid, tid) == SMR_OK)
		{
			//const u64 stamp2 = get_system_time();
			if (SMutexResult res = mutex->m_mutex.trylock(tid))
			{
				if (res != SMR_FAILED)
				{
					goto abort;
				}
				mutex->m_queue.push(tid);

				switch (mutex->m_mutex.lock(tid))
				{
				case SMR_OK:
					mutex->m_queue.invalidate(tid);
				case SMR_SIGNAL:
					break;
				default:
					goto abort;
				}
			}
			mutex->recursive = 1;
			const volatile u64 stamp = cond->signal_stamp;
			cond->signal.unlock(tid);
			Emu.GetCPU().NotifyThread(cond->signaler);
			//ConLog.Write("sys_cond_wait(): signal latency %lld (minimum %lld)", get_system_time() - stamp, stamp2 - stamp);
			return CELL_OK;
		}

		SM_Sleep();

		if (counter++ > max_counter)
		{
			cond->m_queue.invalidate(tid);
			GetCurrentPPUThread().owned_mutexes--; // ???
			return CELL_ETIMEDOUT;
		}
		if (Emu.IsStopped())
		{
			goto abort;
		}
	}

abort:
	sys_cond.Warning("sys_cond_wait(id=%d) aborted", cond_id);
	return CELL_OK;
}