Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement clock_gettime syscall #495

Merged
merged 1 commit into from
Jun 27, 2022

Conversation

rHermes
Copy link
Contributor

@rHermes rHermes commented May 28, 2022

This implements the syscalls for clock_gettime and clock_getres. We
support two clocks: CLOCK_REALTIME and CLOCK_MONOTONIC. I've opted to
use the existing osGetTime() code for the realtime clock, because it's
known to work.

For CLOCK_MONOTONIC I've used svcGetSystemTick() directly, as it has a
higher resolution. We can ignore the drift and so on, because it's
supposed to be just the number of ticks since last boot.

Implementing the clock_gettime syscall allows std::chrono to work, which is the primary reason I started looking at this.

There are two points that I am a bit unsure about:

  • Is it ok to cap the precision of the monotonic clock to 1 microsecond? There are about 268 ticks per microsecond, so we could potentially lower the precision more. I don't know how important this is, but it could be a potential increase.
  • Should I be using the osGetTimeRef() function and osTimeRef_s.value_tick in the monotonic timer? As far as I understand this is just an offset as to when a reference point was set, and is not needed here, but I could be wrong here.

Last, I want to thank you for creating such a library so awesome that even beginner like me can explore the 3ds!

@mtheall
Copy link
Contributor

mtheall commented May 28, 2022

Making your own std::chrono clock is fairly trivial:

#include <3ds.h>

#include <chrono>
#include <cinttypes>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <ratio>

class system_clock
{
public:
	using rep        = std::uint64_t;
	using period     = std::milli;
	using duration   = std::chrono::duration<rep, period>;
	using time_point = std::chrono::time_point<system_clock>;

	static constexpr bool is_steady = false;

	static std::time_t to_time_t(time_point const &t) noexcept
	{
		return std::time_t(std::chrono::duration_cast<std::chrono::seconds>(now().time_since_epoch()).count());
	}

	static time_point from_time_t(std::time_t t) noexcept
	{
		return time_point(duration(t));
	}

	static time_point now() noexcept
	{
		// convert 1900-01-01 epoch to unix epoch
		return time_point(duration(osGetTime() - 2208988800000ULL));
	}
};

class high_resolution_clock
{
public:
	using rep        = std::uint64_t;
	using period     = std::ratio<1, SYSCLOCK_ARM11>;
	using duration   = std::chrono::duration<rep, period>;
	using time_point = std::chrono::time_point<high_resolution_clock>;

	static constexpr bool is_steady = true;

	static time_point now() noexcept
	{
		return time_point(duration(svcGetSystemTick()));
	};
};

using steady_clock = high_resolution_clock;

int main(int argc, char* argv[])
{
	auto const start = high_resolution_clock::now();

	gfxInitDefault();
	consoleInit(GFX_TOP, NULL);

	while (aptMainLoop())
	{
		gspWaitForVBlank();
		gfxSwapBuffers();
		hidScanInput();

		u32 kDown = hidKeysDown();
		if (kDown & KEY_START)
			break;

		if (kDown & KEY_A)
		{
			auto const now = high_resolution_clock::now();
			std::printf("Tick %" PRIu64 ", %lf seconds since start\n",
				now.time_since_epoch().count(),
				std::chrono::duration<double>(now - start).count());
		}

		if (kDown & KEY_B)
		{
			auto const now = system_clock::to_time_t(system_clock::now());
			std::printf("%s", std::ctime(&now));
		}
	}

	gfxExit();
	return 0;
}

@rHermes
Copy link
Contributor Author

rHermes commented May 28, 2022

Thanks for the tip on using a custom implementation, but that seems to me to be the wrong route to take in this case. If we are not interested in implementing clock_gettime, then we should disable the use of it when compiling GCC. There are no warnings as of now that the standard behavior of the C++ library doesn't work.

Your use of the period for the steady_clock is interesting though, and we might be interested in putting those two implementations directly into the GCC directory, but the direct link to libctru would be problematic.

Overall, I think it's best to just implement the syscalls, as it's the most portable and solves the chrono issue in the same instance.

Copy link
Contributor

@mtheall mtheall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately GCC aliases high_resolution_clock to system_clock. If you actually want a high-resolution clock on 3DS with this code then use steady_clock.

libctru/source/system/syscalls.c Outdated Show resolved Hide resolved
libctru/source/system/syscalls.c Outdated Show resolved Hide resolved
libctru/source/system/syscalls.c Outdated Show resolved Hide resolved
@rHermes rHermes force-pushed the implement-clock_gettime branch from f9f4680 to 75473c5 Compare May 29, 2022 11:32
This implements the syscalls for `clock_gettime` and `clock_getres`. We
support two clocks: CLOCK_REALTIME and CLOCK_MONOTONIC. I've opted to
use the existing `osGetTime()` code for the realtime clock, because it's
known to work.

For CLOCK_MONOTONIC I've used `svcGetSystemTick()` directly, as it has a
higher resolution. We can ignore the drift and so on, because it's
supposed to be just the number of ticks since last boot.
@rHermes rHermes force-pushed the implement-clock_gettime branch from 75473c5 to f933d9e Compare May 29, 2022 15:40
@rHermes
Copy link
Contributor Author

rHermes commented Jun 15, 2022

Any update on this?

@fincs fincs merged commit 813d28d into devkitPro:master Jun 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants