[J3] Question about LOCK_TYPE specifications
Brad Richardson
everythingfunctional at protonmail.com
Fri Aug 9 15:33:28 UTC 2024
Hi all,
To continue on this thread, I wrote the following test program to see
what different implementations use as a representation for locked and
unlocked states.
program check_unlocked_uniqueness
use iso_fortran_env, only: lock_type, int8
implicit none
type(lock_type) :: lock1[*], lock2[*]
integer(int8) :: dummy
integer(int8) :: repr(storage_size(lock1)/storage_size(dummy))
if (this_image() == 1) print *, "A lock is ", storage_size(lock1) /
8, " bytes"
lock (lock1[1])
print *, "On image", this_image()
print *, "Initial repr is: "
print *, transfer(lock2[1], repr)
lock (lock2[1])
print *, "While locked is: "
print *, transfer(lock2[1], repr)
unlock (lock2[1])
print *, "After unlocking is: "
print *, transfer(lock2[1], repr)
unlock (lock1[1])
end program
Here is what I was able to observe.
Cray:
* Has a 24 byte lock
* The unlocked state appears to be unique, and all 0
* The locked state sets the 8th bit to 64, and the first bit to the
locking image minus 1
NAG:
* Has a 128 byte lock
* The state of a lock after unlocking is not the default state
* It's not immediately obvious what all the bits mean for locked, but
it appears the first byte is set to 1, and the locking image is byte 17
Intel:
* Has an 8 byte lock
* The state of the lock doesn't appear to change between locked and
unlocked
* The program crashes when run with more than 1 image
OpenCoarrays:
* Has an 8 byte lock
* Crashes immediately, so I can't tell what the implementation looks
like
Given that there is at least one implementation that does not adhere to
"A lock variable is unlocked if and only if the value of each component
is the same as its default value. If it has any other value, it is
locked." I'm inclined to suggest we remove that requirement from the
standard. I've already identified what I think the edits should be (see
below).
What is the appropriate process for this? Can I go ahead and submit an
edit paper for this?
Regards,
Brad
On Thu, 2024-07-25 at 15:00 +0000, Brad Richardson via J3 wrote:
> Hi Malcolm,
>
> Thanks for your response.
>
> On Thu, 2024-07-25 at 07:40 +0900, Malcolm Cohen via J3 wrote:
> > Hi Brad,
> >
> > > A lock variable is unlocked...
> >
> > This is the definition of what is locked or unlocked. It is not a
> > constraint.
> >
> > > All components have default initialization.
> >
> > That is how we achieve the state that lock variables are defined
> > (in
> > the variable definition sense). It has nothing to do with the
> > structure constructor - as you note, you cannot do anything
> > interesting with the constructor; you can still do uninteresting
> > things.
> >
> > Those two specifications work together to achieve lock variables
> > being initially unlocked, no matter how they come into existence.
> >
>
> Why limit so strongly what constitutes an unlocked state? And why
> dictate that a lock variable must be *fully* default initialized?
> There
> are certainly other states an implementation could consider to be
> unlocked and the user wouldn't be able to tell. And surely a
> partially
> defined lock variable could still be usable in a LOCK or UNLOCK
> statement.
>
> > > a bit over specified
> >
> > And your point is?
> >
> > I cannot think of anything user-visible that these requirements and
> > definitions unnecessarily prevent. If have something in mind,
> > please
> > enlighten us.
> >
> > In the absence of user visibility, what is the implementation
> > method
> > that you want to use but which is forbidden by the requirements?
> >
>
> We don't have a design fully fleshed out, but one could imagine how
> something like the following might be useful.
>
> type :: lock_type
> private
> logical :: locked = .false.
> integer :: locked_by
> end type
>
> There is absolutely no reason to require the locked_by component to
> be
> default initialized (it will not be referenced before being locked),
> nor should it be required to be reset when unlocked in order for the
> lock variable to be considered unlocked.
>
> Another important bit is that efficient and fair implementations will
> want to keep track of who is waiting for a lock. I.e.
>
> type :: lock_type
> private
> logical :: locked = .false
> integer :: locked_by
> integer, allocatable :: waiting_for_lock(:)
> end type
>
> But with the prohibition on allocatable components, that
> implementation
> would be invalid. There's a way around the allocatable component, use
> a
> type(c_ptr) component and do stuff in C to work with it, but that
> doesn't quite get around the "only the default state is unlocked". It
> probably wouldn't be a problem, but a user could in theory tell by
> using something like:
>
> print *, transfer(lock_var, [integer::])
> lock (lock_var)
> unlock (lock_var)
> print *, transfer(lock_var, [integer::])
>
> to see that the internal representation isn't necessarily identical
> before and after the lock/unlock sequence.
>
> > I will note that the purpose of these specifications is to ensure
> > that lock variables work the way we want them to. Those
> > specifications are probably sufficient, there is only a point in
> > restricting them to what is strictly necessary if that would enable
> > something useful. It is frequently the case that minimum conditions
> > are more complicated than sufficient conditions, especially when it
> > comes to comprehension and proving sufficiency.
> >
> > > Why must they be *fully* default-initialized?
> >
> > So they have defined values. We do not want holes in the
> > definition.
> > Holes mean the standard does not provide an interpretation. That
> > would make it far more likely that the standard is ambiguous or
> > contradictory. In my opinion, this particular requirement must
> > remain
> > as is.
> >
> > > similar constraint that "Each nonallocatable component is fully
> > > default- initialized."
> >
> > Once again, with feeling: that is not a "constraint". I know that
> > in
> > English there is wide ambiguity so one can use "constraint",
> > "requirement", "rule" and at least a dozen other words
> > simultaneously. When talking about the standard, however, we should
> > strive to use words defined by the standard or by ISO more
> > carefully.
> >
> > Finally, a future revision can always relax requirements without
> > serious risk of incompatibility. Thus, as long as the requirements
> > specify the facility sufficiently well for both users and
> > implementors, "overly strict" is not a problem.
> >
> > Being "insufficiently strict" however is a big problem, for
> > everyone:
> > users, implementors, and future revisions. Tightening restrictions
> > almost always introduces incompatibilities and the likelihood of
> > some
> > implementations having to change their behaviour (possibly even in
> > user-visible ways), or worse: some implementations simply never
> > bother to update their implementation of the feature to the new
> > version, rendering the feature unreliable in actual use. That does
> > not " promote portability, reliability, maintainability, and
> > efficient execution" (our purpose!).
> >
> > And so when I ask "what implementation method is it that you want
> > to
> > use but which is forbidden", I am very much open to any suggestions
> > you might have, but that would be immediately followed by "how do
> > you
> > propose to specify things to provably maintain existing semantics".
>
> The fully default initialized is probably not a problem to satisfy in
> an implementation (if unnecessary), but I really do want to be able
> to
> have a pointer or allocatable component in LOCK_TYPE, and I don't
> want
> to have to fully reset that component for the lock to be considered
> unlocked. I would suggest something like the following edits:
>
> [page 224: paragraph 2] delete the first two sentences:
>
> "A lock variable is unlocked if and only if the value of each
> component
> is the same as its default value. If it has any other value, it is
> locked."
>
> [page 461: paragraph 1] delete the last phrase of the first sentence:
>
> "; no component is allocatable or a pointer"
>
> [page 461: paragraph 1] change the last sentence to:
>
> Each nonallocatable component is fully default-initialized to an
> unlocked state.
>
> [page 461: paragraph 2] change the 3rd and 4th sentences to
>
> The initial state of a lock variable is unlocked.
>
> I'm happy to hear about other options or suggestions, or that an
> implementation need not worry about the backdoor observability using
> `transfer`, so long as it "meets the spirit" of the specification in
> the standard.
>
> Regards,
> Brad
>
> >
> > Cheers,
> > --
> > ..............Malcolm Cohen, NAG Oxford/Tokyo.
> >
> > -----Original Message-----
> > From: J3 <j3-bounces at mailman.j3-fortran.org> On Behalf Of Brad
> > Richardson via J3
> > Sent: Thursday, July 25, 2024 4:48 AM
> > To: General J3 interest list <j3 at mailman.j3-fortran.org>
> > Cc: Brad Richardson <everythingfunctional at protonmail.com>
> > Subject: [J3] Question about LOCK_TYPE specifications
> >
> > Hi all,
> >
> > We were working through some things and ran into a question about
> > some of the constraints and specifications involving LOCK_TYPE,
> > which
> > also tangentially involve EVENT_TYPE, NOTIFY_TYPE and TEAM_TYPE. It
> > seems like they are a bit over specified, so I'd be curious if
> > anybody knows the rationale for some of them.
> >
> > First, from Sec 11.7.10 LOCK and UNLOCK statements its says that
> >
> > > A lock variable is unlocked if and only if the value of each
> > component is the same as its default value. If it has any other
> > value, it is locked.
> >
> > This seems to overly constrain possible implementations. Given that
> > there is no way to determine whether a LOCK_TYPE variable is locked
> > or unlocked other than to try and lock it, what does this
> > constraint
> > achieve? It's worth noting that there's also no other way to change
> > the value of a LOCK_TYPE variable, as LOCK and UNLOCK statements
> > are
> > the only place they can appear in a variable definition context.
> >
> > Next, in 16.10.2.19 LOCK_TYPE it says:
> >
> > > LOCK_TYPE is a derived type with private components; no component
> > > is
> > allocatable or a pointer. It is an extensible type with no type
> > parameters. All components have default initialization.
> > >
> > > ... The unlocked state is represented by the one value that is
> > > the
> > default value of a LOCK_TYPE variable; this is the value specified
> > by
> > the structure constructor LOCK_TYPE ( )
> >
> > Why the restriction to no allocatable or pointer components, or
> > that
> > they all have default initialization? Also, the last sentence
> > implies
> > that it's valid to use the intrinsic structure constructor for
> > LOCK_TYPE with no arguments, but I'm not sure why you'd want that,
> > since you can't do anything meaningful with the value it returns.
> > You
> > can't assign it to anything or pass it as an argument to any
> > procedures since "A named variable with declared type LOCK_TYPE
> > shall
> > be a coarray" and "A lock variable shall not appear in a variable
> > definition context".
> >
> > EVENT_TYPE, NOTIFY_TYPE and TEAM_TYPE are seemingly allowed to have
> > allocatable or pointer components, but still have the similar
> > constraint that "Each nonallocatable component is fully default-
> > initialized." Why must they be *fully* default-initialized? Is this
> > to allow use of intrinsic structure constructors with no arguments?
> > If so why? You can't do anything with the values they return.
> >
> > If anybody has some background info on why these restrictions on an
> > implementation are there I'd greatly appreciate it.
> >
> > Regards,
> > Brad Richardson
> >
> >
>
>
More information about the J3
mailing list