-
Notifications
You must be signed in to change notification settings - Fork 78
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
Basic Key API #3485
base: main
Are you sure you want to change the base?
Basic Key API #3485
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few suggestions, but this looks good.
otherlibs/stdlib_alpha/capsule.mli
Outdated
@@ -44,12 +44,12 @@ | |||
module Name : sig | |||
|
|||
type 'k t : value mod global portable many uncontended unique | |||
(** A ['k Name.t] represents the identity of a capsule. *) | |||
(** A ['k Name.t] represents an identity of a capsule. | |||
A capsule can have multiple names. *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think once something has multiple names it probably isn’t an “identity”. I think, with the design you have here, it would be better to move Name.t
to be Password.Name.t
and describe it as the identity of a password instead.
That would also make it easy to delay creating the name until it is asked for from the password by having the password be mutable and hold a dummy value when created. That will avoid needless contention on the atomic used for allocating names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, maybe Password.Id.t
rather than Password.Name.t
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making the password mutable means it can't easily cross contention, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can just assert that it does. You can see it is semantically fine because the mutable part could be a ref
inside the capsule the password is associated with, so that the password itself constituted permission to access it. It's just that we can't use the Password
API inside the definition of the Password
API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about shared passwords?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, due to val shared : 'k t @ local -> 'k Shared.t @ local @@ portable
we have to use the atomic everywhere (and we need that function to write interfaces general over whether your data is guarded by a mutex or by a rw-lock).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess, we could track whether the shared password is sourced from a normal password or created shared, but that seems overly complicated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it's pretty easy to track, so why not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn’t going to bother storing the atomic in the locks, my suggestion was to have ids only be the ids of passwords not of capsules generally, so we’d just stack allocate a fresh atomic cell for each new password.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. It feels somewhat wrong that just locking a mutex would require a new allocation. I guess we can compensate by adding another version of with_lock
that provides Access.t
instead.
Updated to support |
Updated to only initialize passwords on |
93b7ee8
to
0fe9579
Compare
Updated the API to handle shared keys, |
86c2bf3
to
30c20c5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reviewed everything except the new tests. Will review them later/tomorrow.
|
||
val create_with_rwlock : unit -> Rwlock.packed @@ portable | ||
(** [create_with_rwlock ()] creates a new capsule with an associated reader-writer lock. *) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is probably still worth leaving these in as convenience functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add them to the version in the tree.
let[@inline] with_password (type k) k f = | ||
let pw : k Password.t = Password.unsafe_mk () in | ||
try f pw, k with | ||
| Encapsulated (id, data) as exn -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this should also catch Encapsulated_shared
since the password might get converted to a shared password. The docs should also be updated to reflect this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed shared
instead, since it's either useless or unsound in presence of fork_join
.
otherlibs/stdlib_alpha/capsule.ml
Outdated
| x -> Rw.unlock t.rwlock; x | ||
| exception exn -> | ||
t.poisoned <- true; | ||
t.state <- Frozen; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to set it to Posioned
rather than Frozen
as the exception that is being leaked may mutate the data from the capsule.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
| x -> Rw.unlock t.rwlock; x | ||
| exception exn -> | ||
(* Here we are not poisoning the RwLock, see [capsule.mli] for explanation *) | ||
let exn = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this done before releasing the lock here, but after releasing the lock in with_lock
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistently copied from Mutex
. I think it is a mistake to do it after releasing the lock: there may be another exception raised that modifies the data in the capsule. I changed all functions to convert the exception before the lock is released.
| None -> exn) | ||
| _ -> exn | ||
in | ||
t.state <- Frozen; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is probably worth adding a comment here about why this racy write is ok according to the memory model. The reason it is ok is that:
- All threads which race to write here are all racing to write
Frozen
and the only other write to this field in the program was the initial one that set it toOpen
. - All threads which race to read here do not distinguish between
Open
andFrozen
so do not care which value they see. - The only reads which do care about the difference between
Open
andFrozen
are ones that happen whilst holding the lock for writing -- that means there is a happens before relationship between them and all the writes, so they will reliably see theFrozen
state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment.
otherlibs/stdlib_alpha/capsule.ml
Outdated
| false -> | ||
t.poisoned <- true; | ||
raise Poisoned | ||
| Open | Frozen -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should fail with raise Frozen
if the lock was already frozen. You cannot safely transition from Frozen
to Poisoned
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
30c20c5
to
d024d09
Compare
Basic Capsule Key API.
Adds
'k Key.t
, a ghost type representing the ownership of a capsule'k
. Keys can be converted to passwords or accesses usingwith_password
andaccess
. Exceptions raised from those are unwrapped to the original, destroying the key in process.Mutexes and rw-locks are now created using keys and leak keys on destruction.
Shared keys are not implemented yet, pending discussion how to deal with exceptions.