Skip to content

sflist: SYS_SFLIST_FLAGS_MASK must be a long not an int#17025

Merged
nashif merged 1 commit intozephyrproject-rtos:masterfrom
npitre:sflist
Jun 26, 2019
Merged

sflist: SYS_SFLIST_FLAGS_MASK must be a long not an int#17025
nashif merged 1 commit intozephyrproject-rtos:masterfrom
npitre:sflist

Conversation

@npitre
Copy link

@npitre npitre commented Jun 24, 2019

When splitting the pointer from the flag, ~SYS_SFLIST_FLAGS_MASK remains
a 32-bit value because of the U qualifier. Let's qualify it with UL so
the top half of 64-bit pointers is not truncated.

@zephyrbot zephyrbot added the area: API Changes to public APIs label Jun 24, 2019
Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

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

A U suffix does not truncate long values, it merely qualifies them as unsigned.

Whoops.

@ghost
Copy link

ghost commented Jun 24, 2019

For the record, the U suffix doesn't cause this problem, it's the lack of L that does. Really these sorts of problems should be fixed with casts to intptr_t or somesuch; we're committing the sin of assuming that long and pointer have the same size, which is not guaranteed.

Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

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

I'm not trying to be obstructionist here but I have a vested interest in the LP64 64-bit implementation, so I just want to be sure we get all of these off on the right foot .. some kind of guidelines about the generalization of int/pointer conversions etc.

@andyross
Copy link
Contributor

On a technical level, "long" is going to do the right thing on ILP32 and LP64, which are all the toolchains anyone in this industry cares about support, have supported or will support at any time +/- two decades.

That said, we do have a mandate from the MISRA work to use less variant types, like u32_t. Things like size_t/ssize_t/ptrdiff_t/intptr_t/uintptr_t are not invariantly-sized types, of course. But they probably look more kosher to tools that are going to whine about a "long". I'd say we should pick one of those, but really don't care which.

@ghost
Copy link

ghost commented Jun 24, 2019

@andyross It may be that long does the right thing for our circumstances, but in that case, why are we ever using intptr_t etc. elsewhere? See POINTER_TO_INT and INT_TO_POINTER macros (and their arguably redundant POINTER_TO_UINT and UINT_TO_POINTER macros), and unative_t and then other places just long. We're in the first phase of changes for 64-bit cleanliness, I think we should pick a consistent standard. If that means assuming sizeof(long) == sizeof(void *) then that's fine, but let's just assume that then.

@ghost
Copy link

ghost commented Jun 24, 2019

@andyross Not to mention that something tells me that less than a decade ago, somewhat said "ILP32 is the only mode Zephyr will ever care about", and that's how we ended up with this cleanup to begin with. :)

@npitre
Copy link
Author

npitre commented Jun 25, 2019 via email

@ghost
Copy link

ghost commented Jun 25, 2019

I've used them in the context of thread argument conversion mainly because you cannot cast an int to a void* without going through an intermediate type such as long or intptr_t. So the INT_TO_POINTER() looks nicer than a double cast. And POINTER_TO_INT() is just balancing things out for consistency.

If the integer is the correct size, there's no need for an intermediate cast, right? Under what circumstance do we need to cast an incorrectly-sized integer to a pointer? There's almost no reason to ever do that, and if the need arises, I say we make the programmer double-cast to make the pun obvious.

@ghost
Copy link

ghost commented Jun 25, 2019

IMO, this is more about semantic than anything else. If you know that your integer value always represents a memory address then intptr_t might carry some extra meaning. Otherwise a long is what people use in practice.

I understand what you're saying, but I would say in circumstances where an integer value always represents a memory address, just use a void *. If we're in agreement that long and pointer are always the same size, then let's just dispense entirely with intptr_t et al. (I've never liked them anyway.)

@npitre
Copy link
Author

npitre commented Jun 25, 2019 via email

@ghost
Copy link

ghost commented Jun 25, 2019

You won't find any objection from me there. But I'm not the one to make that call.

Well, you have PRs in that use intptr_t instead of long. So are making the call, so to speak.

Sure. Problem is when you need to perform arithmetics on that value. It's nicer not to have to cast it all the time.

Well, I think we'll both agree just to call it long or unsigned long then :-)

@npitre
Copy link
Author

npitre commented Jun 25, 2019 via email

@npitre
Copy link
Author

npitre commented Jun 25, 2019 via email

@ghost
Copy link

ghost commented Jun 25, 2019

I think it would make sense to just say officially that any pointer that has to be treated as an integer should use an unsigned long type.

We've agreed on this many times. I'm not sure who's been asking you to use intptr_t. Should be discussed and put to bed.

But then people might claim that uintptr_t is shorter. In practice they're (will be) equivalent so really I don't know if this is worth enforcing only one of them.

We can't enforce what people do in the apps they make for Zephyr, but we can enforce coding standards in the project codebase. And, seriously, if people think that typing a few extra characters is too much work .... I really don't even know what to say to that.

When splitting the pointer from the flag, ~SYS_SFLIST_FLAGS_MASK remains
a 32-bit value because of the lack of an L qualifier. Let's qualify it
with UL so the top half of 64-bit pointers is not truncated.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
@npitre
Copy link
Author

npitre commented Jun 25, 2019 via email

@ghost
Copy link

ghost commented Jun 25, 2019

On 64-bit targets, you cannot take an int or an int32_t and cast it to a void* without making the compiler complain at you.

Yes, I know why we're having this conversation :-)

I think it is best to get into the habit of using INT_TO_POINTER() with such interfaces where arguments have to be void* so it is 64-bit clean upfront without having to know why that double cast is needed in the first place.

  1. I think programmers should understand what they're doing and why, and
  2. I don't think that k_thread_create(..., INT_TO_POINTER(1), INT_TO_POINTER(2)) is particularly easier on the eyes than k_thread_create(..., (void *) 1L, (void *) (long) x)... or similar.

If your argument really is that k_create_thread() is slightly less awkward through the use of your macros, I'd like to direct your attention to the fact that the function already takes 10 arguments... however, as a concession to your use-case, perhaps we could engineer some macros that are specifically targeted for arguments that must pass a generic boundary (the void *). ARGUMENT_IN() and ARGUMENT_OUT() or something. Just not INT_TO_POINTER. Use C99 generic macros (shudder) and they could be used without any casts, and even properly preserve doubles and such.

@ghost
Copy link

ghost commented Jun 25, 2019

That's all fine with me. I won't be the one submitting a global s/uintptr_t/unsigned long/g though!

Hah, ok. Maybe I'll work up the chutzpah to do it, then.

I understand that this is probably very frustrating for you, and I'm sorry to barge in on your work like this, but I think it's important that we set a standard sooner rather than later. (Ordinarily I wouldn't be so nitpicky but I've got a personal stake in 64-bit Zephyr.)

@npitre
Copy link
Author

npitre commented Jun 25, 2019 via email

@aescolar
Copy link
Member

any pointer that has to be treated as an integer should use an unsigned long type.

We've agreed on this many times.

@andyross @gnuless @npitre Who, when, where?
(silly things apart) What is the problem with intptr_t?
I would not be surprised if, sometime soon, somebody would try to make a native_win port. And there, long is 32 bits both for win32 and win64.

@npitre
Copy link
Author

npitre commented Jun 25, 2019 via email

@nashif nashif merged commit cf5c22d into zephyrproject-rtos:master Jun 26, 2019
@aescolar
Copy link
Member

Maybe the best solution in that case would still consist of enforcing
a long to be 64 bits for consistency, like #define long DWORD or the like.

@andyross @gnuless @npitre : Wouldn't it be good enough to use void *, in general, (as unknown pointer type), and cast it to uintptr_t when needed (instead of long)?

@npitre
Copy link
Author

npitre commented Jun 26, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: API Changes to public APIs

5 participants