1
2
Fork
You've already forked spin
0

Greedy read lock for RwLock #70

Open
opened 2019年11月14日 18:27:05 +01:00 by Visic · 4 comments
Visic commented 2019年11月14日 18:27:05 +01:00 (Migrated from github.com)
Copy link

I would like to be able to do a greedy readlock on the RwLock.

What I mean by a "greedy" readlock, is that when I call "read" on the RwLock, it increments the reader count (locking out future writers), but the read guard it returns will spin in the deref so that the data access is still safe.

I don't know if this would be of interest to anyone else, but I have hacked away at the RwLock implementation to fit my needs: https://gist.github.com/Visic/5c672c750e1a6e16590d9cd7dd11d1c8

My specific use case is that I need to be able to lock in the current state when some event happens (without blocking that thread), and then leaving it up to the future consumers of the lock to deal with the potential waiting. This also has an added benefit that waiting at that point is less likely, since if the write lock had been taken before, the writer might have finished before you ever actually access the data.

A further improvement could be to implement some sort of "try_deref" on the readlock, so that the readers aren't Forced to wait if they want to poll for the final result.

If this isn't of interest for this crate, please feel free to close the issue.

I would like to be able to do a greedy readlock on the RwLock. What I mean by a "greedy" readlock, is that when I call "read" on the RwLock, it increments the reader count (locking out future writers), but the read guard it returns will spin in the deref so that the data access is still safe. I don't know if this would be of interest to anyone else, but I have hacked away at the RwLock implementation to fit my needs: https://gist.github.com/Visic/5c672c750e1a6e16590d9cd7dd11d1c8 My specific use case is that I need to be able to lock in the current state when some event happens (without blocking that thread), and then leaving it up to the future consumers of the lock to deal with the potential waiting. This also has an added benefit that waiting at that point is less likely, since if the write lock had been taken before, the writer might have finished before you ever actually access the data. A further improvement could be to implement some sort of "try_deref" on the readlock, so that the readers aren't Forced to wait if they want to poll for the final result. If this isn't of interest for this crate, please feel free to close the issue.
ghost commented 2020年10月31日 21:31:13 +01:00 (Migrated from github.com)
Copy link

My specific use case is that I need to be able to lock in the current state when some event happens (without blocking that thread)

I’m interested in potentially implementing this, but I don’t think I’m understanding the use case completely. Looking at the gist, the read implementation doesn’t lock in the current state of the protected data until the Deref finishes spinning (since there may still be a writer mutating the data). Is the idea here just to signal early that future writers should wait so a reader is less likely to wait?

> My specific use case is that I need to be able to lock in the current state when some event happens (without blocking that thread) I’m interested in potentially implementing this, but I don’t think I’m understanding the use case completely. Looking at the gist, the `read` implementation doesn’t lock in the current state of the protected data until the `Deref` finishes spinning (since there may still be a writer mutating the data). Is the idea here just to signal early that future writers should wait so a reader is less likely to wait?
Visic commented 2020年11月03日 18:59:01 +01:00 (Migrated from github.com)
Copy link

You are essentially correct, the most important part of this is to signal that future writers need to wait, however in my system this approach serves a few purposes.

  1. Relatively efficient reading:: When I take a readlock I don't tend to use that state right away, so there is generally time for the active writer to finish up, and my readers won't actually spin when they go to use the data.
  2. Locking in the state at any time:: This includes if I am still currently updating it and goes hand-in-hand with my first point.
  3. Not blocking to get the value until I actually need to use it:: This also goes hand-in-hand with my first point.

To put this into a little context, I have an actor system where each actor exposes some "state". There are events in the system which require me to lock in various actor's states as quickly as possible, and pass those locks onto other actors to be consumed (in their own threads) whenever they can get around to it.

Here is an updated gist with the code I am currently using in production: https://gist.github.com/Visic/3c951421b1e15c809f207dfa34bff30e

Edit: This approach has been working wonderfully for me for almost a year now. I am happy to answer questions, but I might need to be a little vague as to the actual system I am working on.

You are essentially correct, the most important part of this _is_ to signal that future writers need to wait, however in my system this approach serves a few purposes. 1. **Relatively efficient reading**:: When I take a readlock I don't tend to use that state right away, so there is generally time for the active writer to finish up, and my readers won't actually spin when they go to use the data. 2. **Locking in the state at any time**:: This includes if I am still currently updating it and goes hand-in-hand with my first point. 3. **Not blocking to get the value until I actually need to use it**:: This also goes hand-in-hand with my first point. To put this into a little context, I have an actor system where each actor exposes some "state". There are events in the system which require me to lock in various actor's states as quickly as possible, and pass those locks onto other actors to be consumed (in their own threads) whenever they can get around to it. Here is an updated gist with the code I am currently using in production: https://gist.github.com/Visic/3c951421b1e15c809f207dfa34bff30e Edit: This approach has been working wonderfully for me for almost a year now. I am happy to answer questions, but I might need to be a little vague as to the actual system I am working on.
ghost commented 2020年11月07日 19:55:55 +01:00 (Migrated from github.com)
Copy link

Awesome, thanks for the explanation! I guess I'm still a little confused on this part:

Locking in the state at any time:: This includes if I am still currently updating it and goes hand-in-hand with my first point.

Based on the implementation you have in the gist and your description, I don't fully understand what you mean by "locking in the state", since a current writer isn't prevented from writing after read returns but before deref returns. It's might just be that a different API is needed for this, but it seems like this breaks the semantics of read.

Awesome, thanks for the explanation! I guess I'm still a little confused on this part: > Locking in the state at any time:: This includes if I am still currently updating it and goes hand-in-hand with my first point. Based on the implementation you have in the gist and your description, I don't fully understand what you mean by "locking in the state", since a current writer isn't prevented from writing after `read` returns but before `deref` returns. It's might just be that a different API is needed for this, but it seems like this breaks the semantics of `read`.
Visic commented 2020年11月10日 20:40:42 +01:00 (Migrated from github.com)
Copy link

I think I understand your confusion, in the new gist you'll see I removed the write guard in favor of a "try_update" method, meaning that a write lock can only be used for a single update operation. This equates to "locking in the state" since the only write that can be performed after a read lock is requested, is one that is already in progress.

I'm sure the same could be done if I gave out write locks still by only using them for a single update before dropping the lock, so with the original interface, that point is really more about how I was using it rather than the actual guarentees, and it would be more accurate to just say that a new writer couldn't be given out once a reader was requested.

I think I understand your confusion, in the new gist you'll see I removed the write guard in favor of a "try_update" method, meaning that a write lock can only be used for a single update operation. This equates to "locking in the state" since the only write that can be performed after a read lock is requested, is one that is already in progress. I'm sure the same could be done if I gave out write locks still by only using them for a single update before dropping the lock, so with the original interface, that point is really more about how I was using it rather than the actual guarentees, and it would be more accurate to just say that a new writer couldn't be given out once a reader was requested.
Sign in to join this conversation.
No Branch/Tag specified
master
fixes
fix-ub
revert-127-ci
spinlock
No results found.
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
zesterer/spin#70
Reference in a new issue
zesterer/spin
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?