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

Use lock for map iteration #1366

Merged
merged 4 commits into from
Jul 22, 2021
Merged

Use lock for map iteration #1366

merged 4 commits into from
Jul 22, 2021

Conversation

joshwd36
Copy link
Contributor

This Pull Request fixes #1092.

It changes the following:

  • Changes the key of OrderedMap to a MapKey, which contains either a key or an empty value.
  • Allows locking a map to allow safe iteration.
  • When an entry is deleted while the map is locked, it replaces the entry with an empty value to maintain the indexes.
  • When the map is unlocked any empty entries are deleted.
  • Locks are implemented as a guard object so that they are released even if an error occurs.

Concerns/Questions

  • Due to the requirements of the indexmap::Equivalent trait OrderedMap now has the key as a Value rather than a generic parameter. I think this should be fine as I don't think there are other uses of OrderedMap that might require a different key. It may even be possible to implement the Equivalent trait in such a way that it allows the generic parameter to be maintained, but I couldn't find one with a fair amount of experimentation.
  • I think the implementation of the MapLock struct is sound, but there might be a better way to do it. I originally just had methods to lock and unlock that would be called before and after iteration, but if an error occured during iteration the unlock function would never be called.
  • Are there other ways to trigger this behaviour? Perhaps should all methods on map call lock?

@joshwd36
Copy link
Contributor Author

I'm not quite sure why that test is failing. It's not something I've touched with my code and I can't reproduce on my Windows machine.

@HalidOdat HalidOdat added the builtins PRs and Issues related to builtins/intrinsics label Jul 21, 2021
@HalidOdat HalidOdat added this to the v0.13.0 milestone Jul 21, 2021
Copy link
Member

@HalidOdat HalidOdat left a comment

Choose a reason for hiding this comment

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

Looks good to me :)

@joshwd36
Copy link
Contributor Author

Looks good to me :)

Thank you!

One change I had considered but not implemented was instead of using a lock count, and the guard having a GcObject pointing to the map, it might be better to use just an Rc<()> and use the strong count as the lock. Do you think it's worth implementing that?

@HalidOdat
Copy link
Member

One change I had considered but not implemented was instead of using a lock count, and the guard having a GcObject pointing to the map, it might be better to use just an Rc<()> and use the strong count as the lock. Do you think it's worth implementing that?

Yes. I think this is much better idea

@HalidOdat
Copy link
Member

Due to the requirements of the indexmap::Equivalent trait OrderedMap now has the key as a Value rather than a generic parameter. I think this should be fine as I don't think there are other uses of OrderedMap that might require a different key. It may even be possible to implement the Equivalent trait in such a way that it allows the generic parameter to be maintained, but I couldn't find one with a fair amount of experimentation.

I don't think is a problem, at least for now, If we wanted to implement WeakMap but for this we need to write our own garbage collector to support weak pointers so... it is further away in the future.

I think the implementation of the MapLock struct is sound, but there might be a better way to do it. I originally just had methods to lock and unlock that would be called before and after iteration, but if an error occured during iteration the unlock function would never be called.

It seems sound to me as well, And I think having the struct guard is the easier and cleaner way to do it.

@joshwd36
Copy link
Contributor Author

One change I had considered but not implemented was instead of using a lock count, and the guard having a GcObject pointing to the map, it might be better to use just an Rc<()> and use the strong count as the lock. Do you think it's worth implementing that?

Yes. I think this is much better idea

Actually, I've just had a go at implementing this and come up against the obstacle that there would be no way to trigger the retain function when the count reached 1 using Rc<()>.

@HalidOdat
Copy link
Member

Actually, I've just had a go at implementing this and come up against the obstacle that there would be no way to trigger the retain function when the count reached 1 using Rc<()>.

Hmmm. Yeah that is a problem. In that case lets leave it with this implementation.

@HalidOdat
Copy link
Member

HalidOdat commented Jul 21, 2021

I don't think is a problem, at least for now, If we wanted to implement WeakMap but for this we need to write our own garbage collector to support weak pointers so... it is further away in the future.

On more inspection WeakMap does not have functions like values/keys/forEach it only has set/get/has/delete so order and locking is not needed, and we could use std HashMap

@HalidOdat
Copy link
Member

Test result master count PR count difference
Total 78,897 78,897 0
Passed 27,954 27,974 +20
Ignored 15,616 15,616 0
Failed 35,327 35,307 -20
Panics 0 0 0
Conformance 35.43% 35.46% +0.03%
Fixed tests:
test/built-ins/Map/prototype/entries/does-not-have-mapdata-internal-slot-set.js [strict mode] (previously Failed)
test/built-ins/Map/prototype/entries/does-not-have-mapdata-internal-slot-set.js (previously Failed)
test/built-ins/Map/prototype/entries/does-not-have-mapdata-internal-slot.js [strict mode] (previously Failed)
test/built-ins/Map/prototype/entries/does-not-have-mapdata-internal-slot.js (previously Failed)
test/built-ins/Map/prototype/entries/this-not-object-throw.js [strict mode] (previously Failed)
test/built-ins/Map/prototype/entries/this-not-object-throw.js (previously Failed)
test/built-ins/Map/prototype/values/does-not-have-mapdata-internal-slot-set.js [strict mode] (previously Failed)
test/built-ins/Map/prototype/values/does-not-have-mapdata-internal-slot-set.js (previously Failed)
test/built-ins/Map/prototype/values/does-not-have-mapdata-internal-slot.js [strict mode] (previously Failed)
test/built-ins/Map/prototype/values/does-not-have-mapdata-internal-slot.js (previously Failed)
test/built-ins/Map/prototype/values/this-not-object-throw.js [strict mode] (previously Failed)
test/built-ins/Map/prototype/values/this-not-object-throw.js (previously Failed)
test/built-ins/Map/prototype/keys/does-not-have-mapdata-internal-slot-set.js [strict mode] (previously Failed)
test/built-ins/Map/prototype/keys/does-not-have-mapdata-internal-slot-set.js (previously Failed)
test/built-ins/Map/prototype/keys/does-not-have-mapdata-internal-slot.js [strict mode] (previously Failed)
test/built-ins/Map/prototype/keys/does-not-have-mapdata-internal-slot.js (previously Failed)
test/built-ins/Map/prototype/keys/this-not-object-throw.js [strict mode] (previously Failed)
test/built-ins/Map/prototype/keys/this-not-object-throw.js (previously Failed)
test/built-ins/Map/prototype/forEach/iterates-values-deleted-then-readded.js [strict mode] (previously Failed)
test/built-ins/Map/prototype/forEach/iterates-values-deleted-then-readded.js (previously Failed)

@HalidOdat HalidOdat merged commit 711e04d into boa-dev:master Jul 22, 2021
@HalidOdat HalidOdat added the bug Something isn't working label Jul 22, 2021
@joshwd36 joshwd36 deleted the map-lock branch July 22, 2021 11:57
@RageKnify
Copy link
Member

Hey @joshwd36 you interested on also solving this for Set?

@joshwd36
Copy link
Contributor Author

Hey @joshwd36 you interested on also solving this for Set?

I should be able to, I'll see if I have any time

@joshwd36
Copy link
Contributor Author

Hey @joshwd36 you interested on also solving this for Set?

Sorry for the delay, I've done this now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working builtins PRs and Issues related to builtins/intrinsics
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deleting elements from a map during forEach results in invalid iteration
3 participants