mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-05 06:21:26 +12:00
sys_timer...
This commit is contained in:
parent
6537909fd2
commit
e3e4decabf
2 changed files with 186 additions and 183 deletions
|
@ -8,6 +8,8 @@
|
||||||
#include "sys_process.h"
|
#include "sys_process.h"
|
||||||
#include "sys_timer.h"
|
#include "sys_timer.h"
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace vm { using namespace ps3; }
|
namespace vm { using namespace ps3; }
|
||||||
|
|
||||||
logs::channel sys_timer("sys_timer", logs::level::notice);
|
logs::channel sys_timer("sys_timer", logs::level::notice);
|
||||||
|
@ -16,283 +18,287 @@ extern u64 get_system_time();
|
||||||
|
|
||||||
void lv2_timer::on_task()
|
void lv2_timer::on_task()
|
||||||
{
|
{
|
||||||
//thread_lock lock(*this);
|
while (true)
|
||||||
LV2_LOCK;
|
|
||||||
|
|
||||||
while (state <= SYS_TIMER_STATE_RUN)
|
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
const u32 _state = state;
|
||||||
|
|
||||||
if (state == SYS_TIMER_STATE_RUN)
|
if (_state == SYS_TIMER_STATE_RUN)
|
||||||
{
|
{
|
||||||
//LV2_LOCK;
|
const u64 _now = get_system_time();
|
||||||
|
const u64 next = expire;
|
||||||
|
|
||||||
while (get_system_time() >= expire)
|
if (_now >= next)
|
||||||
{
|
{
|
||||||
const auto queue = port.lock();
|
semaphore_lock lock(mutex);
|
||||||
|
|
||||||
if (queue)
|
if (const auto queue = port.lock())
|
||||||
{
|
{
|
||||||
queue->send(source, data1, data2, expire);
|
queue->send(source, data1, data2, next);
|
||||||
|
|
||||||
|
if (period)
|
||||||
|
{
|
||||||
|
// Set next expiration time and check again (HACK)
|
||||||
|
expire += period;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (period && queue)
|
// Stop: oneshot or the event port was disconnected (TODO: is it correct?)
|
||||||
{
|
state = SYS_TIMER_STATE_STOP;
|
||||||
expire += period; // set next expiration time
|
continue;
|
||||||
|
|
||||||
continue; // hack: check again
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state = SYS_TIMER_STATE_STOP; // stop if oneshot or the event port was disconnected (TODO: is it correct?)
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
LV2_UNLOCK, thread_ctrl::wait_for(1000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string lv2_timer::get_name() const
|
|
||||||
{
|
|
||||||
return fmt::format("Timer Thread[0x%x]", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void lv2_timer::on_stop()
|
void lv2_timer::on_stop()
|
||||||
{
|
{
|
||||||
// Signal thread using invalid state and join
|
// Signal thread using invalid state
|
||||||
state = -1;
|
state = -1;
|
||||||
this->notify();
|
notify();
|
||||||
named_thread::on_stop();
|
join();
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 sys_timer_create(vm::ptr<u32> timer_id)
|
error_code sys_timer_create(vm::ptr<u32> timer_id)
|
||||||
{
|
{
|
||||||
sys_timer.warning("sys_timer_create(timer_id=*0x%x)", timer_id);
|
sys_timer.warning("sys_timer_create(timer_id=*0x%x)", timer_id);
|
||||||
|
|
||||||
*timer_id = idm::make<lv2_obj, lv2_timer>();
|
if (const u32 id = idm::make<lv2_obj, lv2_timer>())
|
||||||
|
{
|
||||||
return CELL_OK;
|
*timer_id = id;
|
||||||
|
return CELL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CELL_EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 sys_timer_destroy(u32 timer_id)
|
error_code sys_timer_destroy(u32 timer_id)
|
||||||
{
|
{
|
||||||
sys_timer.warning("sys_timer_destroy(timer_id=0x%x)", timer_id);
|
sys_timer.warning("sys_timer_destroy(timer_id=0x%x)", timer_id);
|
||||||
|
|
||||||
LV2_LOCK;
|
const auto timer = idm::withdraw<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError
|
||||||
|
{
|
||||||
|
semaphore_lock lock(timer.mutex);
|
||||||
|
|
||||||
const auto timer = idm::get<lv2_obj, lv2_timer>(timer_id);
|
if (!timer.port.expired())
|
||||||
|
{
|
||||||
|
return CELL_EISCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
if (!timer)
|
if (!timer)
|
||||||
{
|
{
|
||||||
return CELL_ESRCH;
|
return CELL_ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!timer->port.expired())
|
if (timer.ret)
|
||||||
{
|
{
|
||||||
return CELL_EISCONN;
|
return timer.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
idm::remove<lv2_obj, lv2_timer>(timer_id);
|
|
||||||
|
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 sys_timer_get_information(u32 timer_id, vm::ptr<sys_timer_information_t> info)
|
error_code sys_timer_get_information(u32 timer_id, vm::ptr<sys_timer_information_t> info)
|
||||||
{
|
{
|
||||||
sys_timer.warning("sys_timer_get_information(timer_id=0x%x, info=*0x%x)", timer_id, info);
|
sys_timer.trace("sys_timer_get_information(timer_id=0x%x, info=*0x%x)", timer_id, info);
|
||||||
|
|
||||||
LV2_LOCK;
|
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer)
|
||||||
|
{
|
||||||
|
semaphore_lock lock(timer.mutex);
|
||||||
|
|
||||||
const auto timer = idm::get<lv2_obj, lv2_timer>(timer_id);
|
info->next_expire = timer.expire;
|
||||||
|
info->period = timer.period;
|
||||||
|
info->timer_state = timer.state;
|
||||||
|
});
|
||||||
|
|
||||||
if (!timer)
|
if (!timer)
|
||||||
{
|
{
|
||||||
return CELL_ESRCH;
|
return CELL_ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->next_expiration_time = timer->expire;
|
|
||||||
|
|
||||||
info->period = timer->period;
|
|
||||||
info->timer_state = timer->state;
|
|
||||||
|
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 _sys_timer_start(u32 timer_id, u64 base_time, u64 period)
|
error_code _sys_timer_start(u32 timer_id, u64 base_time, u64 period)
|
||||||
{
|
{
|
||||||
sys_timer.warning("_sys_timer_start(timer_id=0x%x, base_time=0x%llx, period=0x%llx)", timer_id, base_time, period);
|
sys_timer.trace("_sys_timer_start(timer_id=0x%x, base_time=0x%llx, period=0x%llx)", timer_id, base_time, period);
|
||||||
|
|
||||||
const u64 start_time = get_system_time();
|
const u64 start_time = get_system_time();
|
||||||
|
|
||||||
LV2_LOCK;
|
if (!period && start_time >= base_time)
|
||||||
|
{
|
||||||
|
// Invalid oneshot (TODO: what will happen if both args are 0?)
|
||||||
|
return CELL_ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (period && period < 100)
|
||||||
|
{
|
||||||
|
// Invalid periodic timer
|
||||||
|
return CELL_EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
const auto timer = idm::get<lv2_obj, lv2_timer>(timer_id);
|
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError
|
||||||
|
{
|
||||||
|
semaphore_lock lock(timer.mutex);
|
||||||
|
|
||||||
|
if (timer.state != SYS_TIMER_STATE_STOP)
|
||||||
|
{
|
||||||
|
return CELL_EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timer.port.expired())
|
||||||
|
{
|
||||||
|
return CELL_ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
if (!timer)
|
||||||
{
|
{
|
||||||
return CELL_ESRCH;
|
return CELL_ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer->state != SYS_TIMER_STATE_STOP)
|
if (timer.ret)
|
||||||
{
|
{
|
||||||
return CELL_EBUSY;
|
return timer.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!period)
|
|
||||||
{
|
|
||||||
// oneshot timer (TODO: what will happen if both args are 0?)
|
|
||||||
|
|
||||||
if (start_time >= base_time)
|
|
||||||
{
|
|
||||||
return CELL_ETIMEDOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// periodic timer
|
|
||||||
|
|
||||||
if (period < 100)
|
|
||||||
{
|
|
||||||
return CELL_EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timer->port.expired())
|
|
||||||
{
|
|
||||||
return CELL_ENOTCONN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 sys_timer_stop(u32 timer_id)
|
error_code sys_timer_stop(u32 timer_id)
|
||||||
{
|
{
|
||||||
sys_timer.warning("sys_timer_stop()");
|
sys_timer.trace("sys_timer_stop()");
|
||||||
|
|
||||||
LV2_LOCK;
|
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [](lv2_timer& timer)
|
||||||
|
{
|
||||||
|
semaphore_lock lock(timer.mutex);
|
||||||
|
|
||||||
const auto timer = idm::get<lv2_obj, lv2_timer>(timer_id);
|
timer.state = SYS_TIMER_STATE_STOP;
|
||||||
|
});
|
||||||
|
|
||||||
if (!timer)
|
if (!timer)
|
||||||
{
|
{
|
||||||
return CELL_ESRCH;
|
return CELL_ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
timer->state = SYS_TIMER_STATE_STOP; // stop timer
|
|
||||||
|
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 sys_timer_connect_event_queue(u32 timer_id, u32 queue_id, u64 name, u64 data1, u64 data2)
|
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);
|
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);
|
||||||
|
|
||||||
LV2_LOCK;
|
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError
|
||||||
|
|
||||||
const auto timer = idm::get<lv2_obj, lv2_timer>(timer_id);
|
|
||||||
const auto queue = idm::get<lv2_obj, lv2_event_queue>(queue_id);
|
|
||||||
|
|
||||||
if (!timer || !queue)
|
|
||||||
{
|
{
|
||||||
return CELL_ESRCH;
|
const auto found = idm::find_unlocked<lv2_obj, lv2_event_queue>(queue_id);
|
||||||
}
|
|
||||||
|
|
||||||
if (!timer->port.expired())
|
if (!found)
|
||||||
{
|
{
|
||||||
return CELL_EISCONN;
|
return CELL_ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
timer->port = queue; // connect event queue
|
semaphore_lock lock(timer.mutex);
|
||||||
timer->source = name ? name : ((u64)process_getpid() << 32) | timer_id;
|
|
||||||
timer->data1 = data1;
|
|
||||||
timer->data2 = data2;
|
|
||||||
|
|
||||||
return CELL_OK;
|
if (!timer.port.expired())
|
||||||
}
|
{
|
||||||
|
return CELL_EISCONN;
|
||||||
|
}
|
||||||
|
|
||||||
s32 sys_timer_disconnect_event_queue(u32 timer_id)
|
// Connect event queue
|
||||||
{
|
timer.port = std::static_pointer_cast<lv2_event_queue>(found->second);
|
||||||
sys_timer.warning("sys_timer_disconnect_event_queue(timer_id=0x%x)", timer_id);
|
timer.source = name ? name : ((u64)process_getpid() << 32) | timer_id;
|
||||||
|
timer.data1 = data1;
|
||||||
LV2_LOCK;
|
timer.data2 = data2;
|
||||||
|
return {};
|
||||||
const auto timer = idm::get<lv2_obj, lv2_timer>(timer_id);
|
});
|
||||||
|
|
||||||
if (!timer)
|
if (!timer)
|
||||||
{
|
{
|
||||||
return CELL_ESRCH;
|
return CELL_ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer->port.expired())
|
if (timer.ret)
|
||||||
{
|
{
|
||||||
return CELL_ENOTCONN;
|
return timer.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
timer->port.reset(); // disconnect event queue
|
|
||||||
timer->state = SYS_TIMER_STATE_STOP; // stop timer
|
|
||||||
|
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <thread>
|
error_code sys_timer_disconnect_event_queue(u32 timer_id)
|
||||||
|
|
||||||
s32 sys_timer_sleep(u32 sleep_time)
|
|
||||||
{
|
{
|
||||||
sys_timer.trace("sys_timer_sleep(sleep_time=%d)", sleep_time);
|
sys_timer.warning("sys_timer_disconnect_event_queue(timer_id=0x%x)", timer_id);
|
||||||
|
|
||||||
const u64 start_time = get_system_time();
|
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [](lv2_timer& timer) -> CellError
|
||||||
|
|
||||||
const u64 useconds = sleep_time * 1000000ull;
|
|
||||||
|
|
||||||
u64 passed;
|
|
||||||
|
|
||||||
while (useconds > (passed = get_system_time() - start_time) + 1000)
|
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
semaphore_lock lock(timer.mutex);
|
||||||
|
|
||||||
std::this_thread::sleep_for(1ms);
|
if (timer.port.expired())
|
||||||
}
|
{
|
||||||
|
return CELL_ENOTCONN;
|
||||||
if (useconds > passed)
|
}
|
||||||
|
|
||||||
|
timer.state = SYS_TIMER_STATE_STOP;
|
||||||
|
timer.port.reset();
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!timer)
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(useconds - passed));
|
return CELL_ESRCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timer.ret)
|
||||||
|
{
|
||||||
|
return timer.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_EMU_STATUS;
|
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 sys_timer_usleep(const u64 sleep_time)
|
error_code sys_timer_sleep(u32 sleep_time)
|
||||||
|
{
|
||||||
|
sys_timer.trace("sys_timer_sleep(sleep_time=%d) -> sys_timer_usleep()", sleep_time);
|
||||||
|
|
||||||
|
return sys_timer_usleep(sleep_time * u64{1000000});
|
||||||
|
}
|
||||||
|
|
||||||
|
error_code sys_timer_usleep(u64 sleep_time)
|
||||||
{
|
{
|
||||||
sys_timer.trace("sys_timer_usleep(sleep_time=0x%llx)", sleep_time);
|
sys_timer.trace("sys_timer_usleep(sleep_time=0x%llx)", sleep_time);
|
||||||
|
|
||||||
const u64 start_time = get_system_time();
|
u64 start = get_system_time();
|
||||||
|
u64 passed = 0;
|
||||||
|
|
||||||
u64 passed;
|
// SLEEP
|
||||||
|
|
||||||
while (sleep_time > (passed = get_system_time() - start_time) + 1000)
|
while (sleep_time >= passed)
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
// TODO: use single global dedicated thread for busy waiting
|
||||||
|
thread_ctrl::wait_for(std::max<u64>(1, sleep_time - passed));
|
||||||
std::this_thread::sleep_for(1ms);
|
passed = get_system_time() - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sleep_time > passed)
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(sleep_time - passed));
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK_EMU_STATUS;
|
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,42 +11,39 @@ enum : u32
|
||||||
|
|
||||||
struct sys_timer_information_t
|
struct sys_timer_information_t
|
||||||
{
|
{
|
||||||
be_t<s64> next_expiration_time;
|
be_t<s64> next_expire;
|
||||||
be_t<u64> period;
|
be_t<u64> period;
|
||||||
be_t<u32> timer_state;
|
be_t<u32> timer_state;
|
||||||
be_t<u32> pad;
|
be_t<u32> pad;
|
||||||
};
|
};
|
||||||
|
|
||||||
class lv2_timer final : public lv2_obj, public named_thread
|
struct lv2_timer final : public lv2_obj, public named_thread
|
||||||
{
|
{
|
||||||
void on_task() override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const u32 id_base = 0x11000000;
|
static const u32 id_base = 0x11000000;
|
||||||
|
|
||||||
std::string get_name() const override;
|
void on_task() override;
|
||||||
|
|
||||||
void on_stop() override;
|
void on_stop() override;
|
||||||
|
|
||||||
const u32 id = idm::last_id();
|
semaphore<> mutex;
|
||||||
|
atomic_t<u32> state{SYS_TIMER_STATE_RUN};
|
||||||
|
|
||||||
atomic_t<u32> state{ SYS_TIMER_STATE_RUN }; // Timer state
|
std::weak_ptr<lv2_event_queue> port;
|
||||||
|
u64 source;
|
||||||
std::weak_ptr<lv2_event_queue> port; // Event queue
|
u64 data1;
|
||||||
u64 source; // Event source
|
u64 data2;
|
||||||
u64 data1; // Event arg 1
|
|
||||||
u64 data2; // Event arg 2
|
|
||||||
|
|
||||||
u64 expire = 0; // Next expiration time
|
atomic_t<u64> expire{0}; // Next expiration time
|
||||||
u64 period = 0; // Period (oneshot if 0)
|
atomic_t<u64> period{0}; // Period (oneshot if 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
s32 sys_timer_create(vm::ps3::ptr<u32> timer_id);
|
// Syscalls
|
||||||
s32 sys_timer_destroy(u32 timer_id);
|
|
||||||
s32 sys_timer_get_information(u32 timer_id, vm::ps3::ptr<sys_timer_information_t> info);
|
error_code sys_timer_create(vm::ps3::ptr<u32> timer_id);
|
||||||
s32 _sys_timer_start(u32 timer_id, u64 basetime, u64 period); // basetime type changed from s64
|
error_code sys_timer_destroy(u32 timer_id);
|
||||||
s32 sys_timer_stop(u32 timer_id);
|
error_code sys_timer_get_information(u32 timer_id, vm::ps3::ptr<sys_timer_information_t> info);
|
||||||
s32 sys_timer_connect_event_queue(u32 timer_id, u32 queue_id, u64 name, u64 data1, u64 data2);
|
error_code _sys_timer_start(u32 timer_id, u64 basetime, u64 period); // basetime type changed from s64
|
||||||
s32 sys_timer_disconnect_event_queue(u32 timer_id);
|
error_code sys_timer_stop(u32 timer_id);
|
||||||
s32 sys_timer_sleep(u32 sleep_time);
|
error_code sys_timer_connect_event_queue(u32 timer_id, u32 queue_id, u64 name, u64 data1, u64 data2);
|
||||||
s32 sys_timer_usleep(u64 sleep_time);
|
error_code sys_timer_disconnect_event_queue(u32 timer_id);
|
||||||
|
error_code sys_timer_sleep(u32 sleep_time);
|
||||||
|
error_code sys_timer_usleep(u64 sleep_time);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue