2019-03-26 20:08:49 +01:00
|
|
|
|
#include "stdafx.h"
|
2018-09-29 00:12:00 +02:00
|
|
|
|
#include "sys_timer.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"
|
2013-11-09 02:05:58 +01:00
|
|
|
|
|
2017-02-04 15:00:02 +01:00
|
|
|
|
#include <thread>
|
|
|
|
|
|
|
2018-08-25 14:39:00 +02:00
|
|
|
|
LOG_CHANNEL(sys_timer);
|
2013-11-09 02:05:58 +01:00
|
|
|
|
|
2019-07-14 05:55:11 +02:00
|
|
|
|
extern u64 get_guest_system_time();
|
2015-07-06 01:21:15 +02:00
|
|
|
|
|
2018-10-11 00:17:19 +02:00
|
|
|
|
void lv2_timer_context::operator()()
|
2013-11-09 02:05:58 +01:00
|
|
|
|
{
|
2020-02-29 10:41:16 +01:00
|
|
|
|
while (thread_ctrl::state() != thread_state::aborting)
|
2015-03-08 16:25:31 +01:00
|
|
|
|
{
|
2020-02-29 10:41:16 +01:00
|
|
|
|
if (state == SYS_TIMER_STATE_RUN)
|
2015-03-08 16:25:31 +01:00
|
|
|
|
{
|
2019-07-14 05:55:11 +02:00
|
|
|
|
const u64 _now = get_guest_system_time();
|
2020-02-29 15:15:25 +01:00
|
|
|
|
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
|
|
|
|
{
|
2018-09-03 21:28:33 +02:00
|
|
|
|
std::lock_guard lock(mutex);
|
2015-11-26 09:06:29 +01:00
|
|
|
|
|
2020-02-29 15:15:25 +01:00
|
|
|
|
if (next = expire; _now < next)
|
|
|
|
|
|
{
|
|
|
|
|
|
// expire was updated in the middle, don't send an event
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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);
|
2019-03-26 20:08:49 +01:00
|
|
|
|
}
|
2017-02-04 15:00:02 +01:00
|
|
|
|
|
2019-03-26 20:08:49 +01:00
|
|
|
|
if (period)
|
|
|
|
|
|
{
|
2020-02-29 15:15:25 +01:00
|
|
|
|
// Set next expiration time and check again
|
2019-03-26 20:08:49 +01:00
|
|
|
|
expire += period;
|
|
|
|
|
|
continue;
|
2015-11-26 09:06:29 +01:00
|
|
|
|
}
|
2016-04-25 12:49:12 +02:00
|
|
|
|
|
2019-03-26 20:08:49 +01:00
|
|
|
|
// Stop after oneshot
|
2020-02-29 10:41:16 +01:00
|
|
|
|
state.release(SYS_TIMER_STATE_STOP);
|
2017-02-04 15:00:02 +01:00
|
|
|
|
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
|
2019-07-14 05:55:11 +02:00
|
|
|
|
lv2_obj::wait_timeout(next - _now);
|
2020-02-29 10:41:16 +01:00
|
|
|
|
continue;
|
2017-02-04 15:00:02 +01:00
|
|
|
|
}
|
2020-02-29 10:41:16 +01:00
|
|
|
|
|
2020-02-29 15:15:25 +01:00
|
|
|
|
thread_ctrl::wait();
|
2015-11-26 09:06:29 +01:00
|
|
|
|
}
|
2015-06-19 17:49:38 +02:00
|
|
|
|
}
|
2015-03-08 16:25:31 +01:00
|
|
|
|
|
2019-07-14 17:21:56 +02:00
|
|
|
|
error_code sys_timer_create(ppu_thread& ppu, vm::ptr<u32> timer_id)
|
2015-06-19 17:49:38 +02:00
|
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
|
ppu.state += cpu_flag::wait;
|
2019-07-14 17:21:56 +02:00
|
|
|
|
|
2016-01-12 22:57:16 +01:00
|
|
|
|
sys_timer.warning("sys_timer_create(timer_id=*0x%x)", timer_id);
|
2015-06-19 17:49:38 +02:00
|
|
|
|
|
2018-10-11 00:17:19 +02:00
|
|
|
|
if (const u32 id = idm::make<lv2_obj, lv2_timer>("Timer Thread"))
|
2017-02-04 15:00:02 +01:00
|
|
|
|
{
|
|
|
|
|
|
*timer_id = id;
|
|
|
|
|
|
return CELL_OK;
|
|
|
|
|
|
}
|
2018-02-09 15:49:37 +01:00
|
|
|
|
|
2017-02-04 15:00:02 +01:00
|
|
|
|
return CELL_EAGAIN;
|
2013-11-09 02:05:58 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-14 17:21:56 +02:00
|
|
|
|
error_code sys_timer_destroy(ppu_thread& ppu, u32 timer_id)
|
2013-11-09 02:05:58 +01:00
|
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
|
ppu.state += cpu_flag::wait;
|
2019-07-14 17:21:56 +02:00
|
|
|
|
|
2016-01-12 22:57:16 +01:00
|
|
|
|
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
|
|
|
|
|
|
{
|
2020-04-09 18:05:43 +02:00
|
|
|
|
if (std::shared_lock lock(timer.mutex); lv2_event_queue::check(timer.port))
|
2017-02-04 15:00:02 +01:00
|
|
|
|
{
|
|
|
|
|
|
return CELL_EISCONN;
|
|
|
|
|
|
}
|
2015-06-21 02:17:42 +02:00
|
|
|
|
|
2020-02-29 10:41:16 +01:00
|
|
|
|
timer = thread_state::aborting;
|
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;
|
|
|
|
|
|
}
|
2013-11-09 02:05:58 +01:00
|
|
|
|
|
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
|
|
|
|
}
|
2013-11-09 02:05:58 +01:00
|
|
|
|
|
|
|
|
|
|
return CELL_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-14 17:21:56 +02:00
|
|
|
|
error_code sys_timer_get_information(ppu_thread& ppu, u32 timer_id, vm::ptr<sys_timer_information_t> info)
|
2013-11-09 02:05:58 +01:00
|
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
|
ppu.state += cpu_flag::wait;
|
2019-07-14 17:21:56 +02:00
|
|
|
|
|
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
|
|
|
|
|
2020-06-09 17:35:14 +02:00
|
|
|
|
sys_timer_information_t _info{};
|
|
|
|
|
|
|
2017-02-04 15:00:02 +01:00
|
|
|
|
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer)
|
|
|
|
|
|
{
|
2020-06-09 17:35:14 +02:00
|
|
|
|
timer.get_information(_info);
|
2017-02-04 15:00:02 +01:00
|
|
|
|
});
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-09 17:35:14 +02:00
|
|
|
|
std::memcpy(info.get_ptr(), &_info, info.size());
|
2013-11-09 02:05:58 +01:00
|
|
|
|
return CELL_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-14 17:21:56 +02:00
|
|
|
|
error_code _sys_timer_start(ppu_thread& ppu, u32 timer_id, u64 base_time, u64 period)
|
2013-11-09 02:05:58 +01:00
|
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
|
ppu.state += cpu_flag::wait;
|
2019-07-14 17:21:56 +02:00
|
|
|
|
|
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);
|
2013-11-09 02:05:58 +01:00
|
|
|
|
|
2019-07-14 05:55:11 +02:00
|
|
|
|
const u64 start_time = get_guest_system_time();
|
2013-11-09 02:05:58 +01:00
|
|
|
|
|
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?)
|
2017-02-06 19:36:46 +01:00
|
|
|
|
return not_an_error(CELL_ETIMEDOUT);
|
2015-03-08 16:25:31 +01:00
|
|
|
|
}
|
2018-02-09 15:49:37 +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
|
|
|
|
{
|
2018-10-11 00:17:19 +02:00
|
|
|
|
std::unique_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;
|
2018-10-11 00:17:19 +02:00
|
|
|
|
|
|
|
|
|
|
lock.unlock();
|
|
|
|
|
|
thread_ctrl::notify(timer);
|
2017-02-04 15:00:02 +01:00
|
|
|
|
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
|
|
|
|
|
2013-11-09 02:05:58 +01:00
|
|
|
|
return CELL_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-14 17:21:56 +02:00
|
|
|
|
error_code sys_timer_stop(ppu_thread& ppu, u32 timer_id)
|
2013-11-09 02:05:58 +01:00
|
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
|
ppu.state += cpu_flag::wait;
|
2019-07-14 17:21:56 +02:00
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2018-09-03 21:28:33 +02:00
|
|
|
|
std::lock_guard 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;
|
|
|
|
|
|
}
|
2013-11-09 02:05:58 +01:00
|
|
|
|
|
|
|
|
|
|
return CELL_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-14 17:21:56 +02:00
|
|
|
|
error_code sys_timer_connect_event_queue(ppu_thread& ppu, u32 timer_id, u32 queue_id, u64 name, u64 data1, u64 data2)
|
2013-11-09 02:05:58 +01:00
|
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
|
ppu.state += cpu_flag::wait;
|
2019-07-14 17:21:56 +02:00
|
|
|
|
|
2016-01-12 22:57:16 +01:00
|
|
|
|
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);
|
2013-11-09 02:05:58 +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
|
|
|
|
|
|
{
|
|
|
|
|
|
const auto found = idm::find_unlocked<lv2_obj, lv2_event_queue>(queue_id);
|
|
|
|
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
|
|
{
|
|
|
|
|
|
return CELL_ESRCH;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-09-03 21:28:33 +02:00
|
|
|
|
std::lock_guard lock(timer.mutex);
|
2015-06-21 02:17:42 +02:00
|
|
|
|
|
2020-04-09 18:05:43 +02:00
|
|
|
|
if (lv2_event_queue::check(timer.port))
|
2017-02-04 15:00:02 +01:00
|
|
|
|
{
|
|
|
|
|
|
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);
|
2019-11-29 23:28:06 +01:00
|
|
|
|
timer.source = name ? name : (s64{process_getpid()} << 32) | u64{timer_id};
|
2017-02-04 15:00:02 +01:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2013-11-09 02:05:58 +01:00
|
|
|
|
return CELL_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-14 17:21:56 +02:00
|
|
|
|
error_code sys_timer_disconnect_event_queue(ppu_thread& ppu, u32 timer_id)
|
2013-11-09 02:05:58 +01:00
|
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
|
ppu.state += cpu_flag::wait;
|
2019-07-14 17:21:56 +02:00
|
|
|
|
|
2016-01-12 22:57:16 +01:00
|
|
|
|
sys_timer.warning("sys_timer_disconnect_event_queue(timer_id=0x%x)", timer_id);
|
2013-11-09 02:05:58 +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
|
|
|
|
|
|
{
|
2018-09-03 21:28:33 +02:00
|
|
|
|
std::lock_guard lock(timer.mutex);
|
2017-02-04 15:00:02 +01:00
|
|
|
|
|
2020-02-29 15:15:25 +01:00
|
|
|
|
timer.state = SYS_TIMER_STATE_STOP;
|
|
|
|
|
|
|
2020-04-09 18:05:43 +02:00
|
|
|
|
if (!lv2_event_queue::check(timer.port))
|
2017-02-04 15:00:02 +01:00
|
|
|
|
{
|
|
|
|
|
|
return CELL_ENOTCONN;
|
|
|
|
|
|
}
|
2015-06-21 02:17:42 +02:00
|
|
|
|
|
2017-02-04 15:00:02 +01:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2013-11-09 02:05:58 +01:00
|
|
|
|
return CELL_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-05 13:48:11 +01:00
|
|
|
|
error_code sys_timer_sleep(ppu_thread& ppu, u32 sleep_time)
|
2013-11-09 02:05:58 +01:00
|
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
|
ppu.state += cpu_flag::wait;
|
2017-03-11 00:14:48 +01:00
|
|
|
|
|
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});
|
2013-11-09 02:05:58 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-05 13:48:11 +01:00
|
|
|
|
error_code sys_timer_usleep(ppu_thread& ppu, u64 sleep_time)
|
2013-11-09 02:05:58 +01:00
|
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
|
ppu.state += cpu_flag::wait;
|
2017-03-11 00:14:48 +01:00
|
|
|
|
|
2016-01-12 22:57:16 +01:00
|
|
|
|
sys_timer.trace("sys_timer_usleep(sleep_time=0x%llx)", sleep_time);
|
2015-03-08 16:25:31 +01:00
|
|
|
|
|
2018-05-26 12:06:37 +02:00
|
|
|
|
if (sleep_time)
|
|
|
|
|
|
{
|
2019-07-30 16:15:15 +02:00
|
|
|
|
lv2_obj::sleep(ppu, sleep_time);
|
2018-05-28 15:52:21 +02:00
|
|
|
|
|
2019-07-14 05:55:11 +02:00
|
|
|
|
lv2_obj::wait_timeout<true>(sleep_time);
|
2018-05-26 12:06:37 +02:00
|
|
|
|
|
2019-07-14 05:55:11 +02:00
|
|
|
|
if (ppu.is_stopped())
|
2018-05-26 12:06:37 +02:00
|
|
|
|
{
|
2019-07-14 05:55:11 +02:00
|
|
|
|
return 0;
|
2018-05-26 12:06:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2015-03-08 16:25:31 +01:00
|
|
|
|
{
|
2019-03-23 08:08:36 +01:00
|
|
|
|
std::this_thread::yield();
|
2015-03-08 16:25:31 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-11-09 02:05:58 +01:00
|
|
|
|
return CELL_OK;
|
2014-07-11 13:59:13 +02:00
|
|
|
|
}
|