rpcsx/rpcs3/Emu/Cell/lv2/sys_timer.cpp

306 lines
5.8 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
2015-03-06 23:58:42 +01:00
#include "Emu/IdManager.h"
2014-08-23 16:51:51 +02:00
2016-04-14 00:23:53 +02:00
#include "Emu/Cell/ErrorCodes.h"
2017-02-05 13:48:11 +01:00
#include "Emu/Cell/PPUThread.h"
2015-03-06 23:24:04 +01:00
#include "sys_event.h"
2015-03-08 16:25:31 +01:00
#include "sys_process.h"
#include "sys_timer.h"
2017-02-04 15:00:02 +01:00
#include <thread>
2016-08-19 23:14:10 +02:00
namespace vm { using namespace ps3; }
2016-05-13 15:55:34 +02:00
logs::channel sys_timer("sys_timer", logs::level::notice);
extern u64 get_system_time();
void lv2_timer::on_task()
{
2017-02-04 15:00:02 +01:00
while (true)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
const u32 _state = state;
2015-03-08 16:25:31 +01:00
2017-02-04 15:00:02 +01:00
if (_state == SYS_TIMER_STATE_RUN)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
const u64 _now = get_system_time();
const u64 next = expire;
2015-07-01 00:25:52 +02:00
2017-02-04 15:00:02 +01:00
if (_now >= next)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
semaphore_lock lock(mutex);
2015-11-26 09:06:29 +01:00
2017-02-04 15:00:02 +01:00
if (const auto queue = port.lock())
2015-11-26 09:06:29 +01:00
{
2017-02-04 15:00:02 +01:00
queue->send(source, data1, data2, next);
if (period)
{
// Set next expiration time and check again (HACK)
expire += period;
continue;
}
2015-11-26 09:06:29 +01:00
}
2017-02-04 15:00:02 +01:00
// Stop: oneshot or the event port was disconnected (TODO: is it correct?)
state = SYS_TIMER_STATE_STOP;
continue;
2015-03-08 16:25:31 +01:00
}
2017-02-04 15:00:02 +01:00
// TODO: use single global dedicated thread for busy waiting, no timer threads
thread_ctrl::wait_for(next - _now);
}
else if (_state == SYS_TIMER_STATE_STOP)
{
thread_ctrl::wait();
}
else
{
break;
2015-03-08 16:25:31 +01:00
}
2015-11-26 09:06:29 +01:00
}
}
2015-03-08 16:25:31 +01:00
void lv2_timer::on_stop()
{
2017-02-04 15:00:02 +01:00
// Signal thread using invalid state
state = -1;
2017-02-04 15:00:02 +01:00
notify();
join();
}
2017-02-04 15:00:02 +01:00
error_code sys_timer_create(vm::ptr<u32> timer_id)
{
sys_timer.warning("sys_timer_create(timer_id=*0x%x)", timer_id);
2017-02-04 15:00:02 +01:00
if (const u32 id = idm::make<lv2_obj, lv2_timer>())
{
*timer_id = id;
return CELL_OK;
}
return CELL_EAGAIN;
}
2017-02-04 15:00:02 +01:00
error_code sys_timer_destroy(u32 timer_id)
{
sys_timer.warning("sys_timer_destroy(timer_id=0x%x)", timer_id);
2015-03-08 16:25:31 +01:00
2017-02-04 15:00:02 +01:00
const auto timer = idm::withdraw<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError
{
semaphore_lock lock(timer.mutex);
if (!timer.port.expired())
{
return CELL_EISCONN;
}
2015-06-21 02:17:42 +02:00
2017-02-04 15:00:02 +01:00
return {};
});
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!timer)
2015-03-08 16:25:31 +01:00
{
return CELL_ESRCH;
}
2017-02-04 15:00:02 +01:00
if (timer.ret)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
return timer.ret;
2015-03-08 16:25:31 +01:00
}
return CELL_OK;
}
2017-02-04 15:00:02 +01:00
error_code sys_timer_get_information(u32 timer_id, vm::ptr<sys_timer_information_t> info)
{
2017-02-04 15:00:02 +01:00
sys_timer.trace("sys_timer_get_information(timer_id=0x%x, info=*0x%x)", timer_id, info);
2015-03-08 16:25:31 +01:00
2017-02-04 15:00:02 +01:00
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer)
{
semaphore_lock lock(timer.mutex);
2015-06-21 02:17:42 +02:00
2017-02-04 15:00:02 +01:00
info->next_expire = timer.expire;
info->period = timer.period;
info->timer_state = timer.state;
});
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!timer)
2015-03-08 16:25:31 +01:00
{
return CELL_ESRCH;
}
return CELL_OK;
}
2017-02-04 15:00:02 +01:00
error_code _sys_timer_start(u32 timer_id, u64 base_time, u64 period)
{
2017-02-04 15:00:02 +01:00
sys_timer.trace("_sys_timer_start(timer_id=0x%x, base_time=0x%llx, period=0x%llx)", timer_id, base_time, period);
2015-03-08 16:25:31 +01:00
const u64 start_time = get_system_time();
2017-02-04 15:00:02 +01:00
if (!period && start_time >= base_time)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
// Invalid oneshot (TODO: what will happen if both args are 0?)
return CELL_ETIMEDOUT;
2015-03-08 16:25:31 +01:00
}
2017-02-04 15:00:02 +01:00
if (period && period < 100)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
// Invalid periodic timer
return CELL_EINVAL;
2015-03-08 16:25:31 +01:00
}
2017-02-04 15:00:02 +01:00
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
semaphore_lock lock(timer.mutex);
2015-03-08 16:25:31 +01:00
2017-02-04 15:00:02 +01:00
if (timer.state != SYS_TIMER_STATE_STOP)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
return CELL_EBUSY;
2015-03-08 16:25:31 +01:00
}
2017-02-04 15:00:02 +01:00
if (timer.port.expired())
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
return CELL_ENOTCONN;
2015-03-08 16:25:31 +01:00
}
2017-02-04 15:00:02 +01:00
// sys_timer_start_periodic() will use current time (TODO: is it correct?)
timer.expire = base_time ? base_time : start_time + period;
timer.period = period;
timer.state = SYS_TIMER_STATE_RUN;
timer.notify();
return {};
});
if (!timer)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
return CELL_ESRCH;
2015-03-08 16:25:31 +01:00
}
2017-02-04 15:00:02 +01:00
if (timer.ret)
{
return timer.ret;
}
2014-07-11 13:59:13 +02:00
return CELL_OK;
}
2017-02-04 15:00:02 +01:00
error_code sys_timer_stop(u32 timer_id)
{
2017-02-04 15:00:02 +01:00
sys_timer.trace("sys_timer_stop()");
2015-03-08 16:25:31 +01:00
2017-02-04 15:00:02 +01:00
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [](lv2_timer& timer)
{
semaphore_lock lock(timer.mutex);
2015-06-21 02:17:42 +02:00
2017-02-04 15:00:02 +01:00
timer.state = SYS_TIMER_STATE_STOP;
});
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!timer)
2015-03-08 16:25:31 +01:00
{
return CELL_ESRCH;
}
return CELL_OK;
}
2017-02-04 15:00:02 +01:00
error_code sys_timer_connect_event_queue(u32 timer_id, u32 queue_id, u64 name, u64 data1, u64 data2)
{
sys_timer.warning("sys_timer_connect_event_queue(timer_id=0x%x, queue_id=0x%x, name=0x%llx, data1=0x%llx, data2=0x%llx)", timer_id, queue_id, name, data1, data2);
2017-02-04 15:00:02 +01:00
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError
{
const auto found = idm::find_unlocked<lv2_obj, lv2_event_queue>(queue_id);
if (!found)
{
return CELL_ESRCH;
}
semaphore_lock lock(timer.mutex);
2015-06-21 02:17:42 +02:00
2017-02-04 15:00:02 +01:00
if (!timer.port.expired())
{
return CELL_EISCONN;
}
2015-03-08 16:25:31 +01:00
2017-02-04 15:00:02 +01:00
// Connect event queue
timer.port = std::static_pointer_cast<lv2_event_queue>(found->second);
timer.source = name ? name : ((u64)process_getpid() << 32) | timer_id;
timer.data1 = data1;
timer.data2 = data2;
return {};
});
if (!timer)
2015-03-08 16:25:31 +01:00
{
return CELL_ESRCH;
}
2017-02-04 15:00:02 +01:00
if (timer.ret)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
return timer.ret;
2015-03-08 16:25:31 +01:00
}
return CELL_OK;
}
2017-02-04 15:00:02 +01:00
error_code sys_timer_disconnect_event_queue(u32 timer_id)
{
sys_timer.warning("sys_timer_disconnect_event_queue(timer_id=0x%x)", timer_id);
2017-02-04 15:00:02 +01:00
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [](lv2_timer& timer) -> CellError
{
semaphore_lock lock(timer.mutex);
if (timer.port.expired())
{
return CELL_ENOTCONN;
}
2015-06-21 02:17:42 +02:00
2017-02-04 15:00:02 +01:00
timer.state = SYS_TIMER_STATE_STOP;
timer.port.reset();
return {};
});
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!timer)
2015-03-08 16:25:31 +01:00
{
return CELL_ESRCH;
}
2017-02-04 15:00:02 +01:00
if (timer.ret)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
return timer.ret;
2015-03-08 16:25:31 +01:00
}
return CELL_OK;
}
2017-02-05 13:48:11 +01:00
error_code sys_timer_sleep(ppu_thread& ppu, u32 sleep_time)
{
2017-02-04 15:00:02 +01:00
sys_timer.trace("sys_timer_sleep(sleep_time=%d) -> sys_timer_usleep()", sleep_time);
2015-03-08 16:25:31 +01:00
2017-02-05 13:48:11 +01:00
return sys_timer_usleep(ppu, sleep_time * u64{1000000});
}
2017-02-05 13:48:11 +01:00
error_code sys_timer_usleep(ppu_thread& ppu, u64 sleep_time)
{
sys_timer.trace("sys_timer_usleep(sleep_time=0x%llx)", sleep_time);
2015-03-08 16:25:31 +01:00
2017-02-05 13:48:11 +01:00
u64 start = ppu.gpr[10] = get_system_time();
2017-02-04 15:00:02 +01:00
u64 passed = 0;
2015-03-08 16:25:31 +01:00
2017-02-04 15:00:02 +01:00
// SLEEP
2015-03-08 16:25:31 +01:00
2017-02-04 15:00:02 +01:00
while (sleep_time >= passed)
2015-03-08 16:25:31 +01:00
{
2017-02-04 15:00:02 +01:00
// TODO: use single global dedicated thread for busy waiting
thread_ctrl::wait_for(std::max<u64>(1, sleep_time - passed));
passed = get_system_time() - start;
2015-03-08 16:25:31 +01:00
}
return CELL_OK;
2014-07-11 13:59:13 +02:00
}