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

[libc] implement localtime #110363

Open
wants to merge 98 commits into
base: main
Choose a base branch
from
Open

[libc] implement localtime #110363

wants to merge 98 commits into from

Conversation

zimirza
Copy link
Contributor

@zimirza zimirza commented Sep 28, 2024

This is an implementation of localtime (currently not finished).

Closes #107597 and #109892

Copy link

github-actions bot commented Sep 28, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

libc/src/time/time_utils.cpp Outdated Show resolved Hide resolved
libc/src/time/time_utils.cpp Outdated Show resolved Hide resolved
libc/src/time/time_utils.cpp Outdated Show resolved Hide resolved
libc/src/time/timezone.cpp Outdated Show resolved Hide resolved
@zimirza
Copy link
Contributor Author

zimirza commented Oct 8, 2024

Instead of using /etc/timezone, it is better to parse /etc/localtime.

Comment on lines 157 to 178
char timezone[TimeConstants::TIMEZONE_SIZE];
char *env_tz = getenv("TZ");
FILE *fp = NULL;
if (env_tz) {
strncpy(timezone, env_tz, sizeof(timezone));
timezone[sizeof(timezone) - 1] = '\0';
} else {
fp = fopen("/etc/timezone", "rb");
if (fp == NULL) {
return time_utils::out_of_range();
}

acquire_file(fp, timezone, TimeConstants::TIMEZONE_SIZE);
}

if (fp != NULL && file_usage == 0) {
release_file(fp, timezone);
return time_utils::out_of_range();
}

int offset = timezone::get_timezone_offset(timezone);
Copy link
Member

Choose a reason for hiding this comment

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

This logic should be moved into an internal utlity function that could be implemented differently for each platform. For example, on baremetal we don't have any filesystem and fopen isn't defined at all. Ideally, this function should return a struct type rather than a string so on platforms like baremetal we can return statically initialized global and avoid having to parse the string on every call (even in the Linux implementation you probably want to memoize it for performance reasons).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I am currently refactoring the logic by parsing /etc/localtime by using the TZif file format.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am now using open instead of fopen to be compatible on other platforms.

Copy link
Member

Choose a reason for hiding this comment

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

It's not about open vs fopen, on platforms like baremetal there's no filesystem at all, so any use file descriptors or I/O operations like read is a non-starter. This needs to be refactored so the common logic can be used across all platforms, and the filesystem bits should live in platforms specific implementation (e.g. src/time/linux). On platforms where filesystem is available, we should be using the internal API like LIBC_NAMESPACE::openfile rather than the POSIX API.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I will refactor I/O operations. I think the best way would be to implement an "interface" such that all platforms could use. Where filesystem is available, I will use LIBC_NAMESPACE::openfile and use the "interface". Is this a good approach? Thank you for the help.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After investigating this, I think I understand the architecture. I will move all Linux to /src/time/linux and will implement a more platform independent approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have now refactored to use the internal API openfile and localtime implementations are in src/time/linux directory. The timezone.cpp/timezone.h files are not in the linux directory. Should these also be there?

Comment on lines 34 to 46
volatile int file_usage = 0;

void release_file(FILE *fp, char *timezone) {
file_usage = 0;
fclose(fp);
}

void acquire_file(FILE *fp, char *timezone, size_t timezone_size) {
while (1) {
if (file_usage == 0) {
file_usage = 1;
break;
}
}

if (fgets(timezone, (int)timezone_size, fp) == NULL) {
release_file(fp, timezone);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

The file related logic should ideally be wrapped in an RAII type to make sure the file is always closed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I have now implemented an RAII correctly. Could you please check?

Copy link
Member

Choose a reason for hiding this comment

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

I don't see any RAII use, have you pushed all your changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have an implementation in time_utils.cpp lines 30-46, where it is possible to get the file and release the file. Please do let me know if I have misunderstood RAII, so I can update this implementation.

@zimirza
Copy link
Contributor Author

zimirza commented Dec 26, 2024

Even though this pull request is not yet finished, I thought it would be great to get a review since this pull request is getting quite big. There are some unit tests that are not working, and have been commented out.

@zimirza zimirza marked this pull request as ready for review December 26, 2024 02:00
@zimirza zimirza force-pushed the localtime_107597 branch 2 times, most recently from eca1d44 to 1456c67 Compare December 26, 2024 06:08
This is an implementation of localtime.

Closes llvm#107597 and llvm#109892
format code with clang-format
update test for `localtime_r`
remove unnecessary code
@nickdesaulniers nickdesaulniers self-requested a review January 7, 2025 18:52
@nickdesaulniers
Copy link
Member

(currently not finished)

Consider marking this as a "draft" in the github UI.

@petrhosek
Copy link
Member

This is a large change and there are some aspects that will likely require more discussion, like what kind of TZ support we even want and how to do it, whether we could share the implementation with libc++, etc. This could be discussed either on Discourse or at the public LLVM-libc meeting (or both). In the meantime, I think the best path forward would be to split this change into multiple changes, with the first one being just an empty minimal implementation which could be merged while we're discussing the implementation details.

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.

[libc] Implement localtime
3 participants