-
Notifications
You must be signed in to change notification settings - Fork 751
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
4.x: Improve the logic of AsyncLock #504
Conversation
var isOwner = false; | ||
lock (queue) | ||
// allow one thread to update the state | ||
lock (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.
Never ever lock on "this"! Code using the AsyncLock in a private field might use it as a lock as well and we're gonna run into some serious trouble.
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.
That's why I noted it in the PR text. Adding a guard = new object()
resolves this with the obvious added cost of allocation. If there was an internal version of this lock and the discipline to not lock on it by Rx code, that would be better. I'll update the PR to use a guard.
Some small review, see it there. Otherwise, I think work on AsyncLock is greatly appreciated. I had a look into this before and was considering an overload that takes an |
// execution succeeded, let's see if more work has to be done | ||
lock (this) | ||
{ | ||
var q = queue; |
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're in a lock, why the local variable?
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 habit from lock-free programming: touch fields as less as possible around atomic accesses.
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 don't think there's much to gain, it just makes the code less readable.
The best would be not to use |
How complex would the inline be? Should it really be repeated throughout the code? |
Something along the lines of #499, but the general structure is something like this: void Trampoline<TState>(ref int wip, ConcurrentQueue<TState> queue,
TState item, Action<TState> genericBody)
{
if (Interlocked.CompareExchange(ref wip, 1, 0) == 0) {
genericBody(item);
if (Interlocked.Decrement(ref wip) == 0) {
return;
}
}
else
{
queue.Enqueue(item);
if (Interlocked.Increment(ref wip) != 1) {
return;
}
}
var missed = 1;
for (;;) {
if (queue.TryDequeue(out var v)) {
genericBody(v);
continue;
}
missed = Interlocked.Add(ref wip, -missed);
if (missed == 0) {
break;
}
}
} where |
Ok, but you wouldn't inline that throughout the whole codebase, would you? |
I did in RxJava a lot because there are no
Yes I would specialize such trampolining for each operator I upgrade. |
Why does it need to be specialized for every operator ? |
If we want optimized operators, they have to be adapted to the special circumstances. |
Let's discuss then if you really see the need to inline trampolining for some operator. AsyncLock may not be the prettiest thing but I think with a overload that takes |
|
You can always add an overload. |
But then the internal structure has to accomodate for a TState object: |
Are we going for this ? |
Not in this PR. |
Ok |
This PR improves the
AsyncLock
class' logic:queue
upfront in case there is no actual concurrency involved.null
.Action
when the "lock" is not held should not go through the queue because it can be executed immediately.this
(asqueue
can now benull
). I've seen other places withguard = new object()
but that's an allocation andthis
is available. Not sure what the policy is given thatAsyncLock
is public. If it wasinternal
or have such copy, I'm sure such shortcuts could be safely taken.