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
DefaultPromise may notify Listeners in wrong order #2157
Conversation
@trustin any good idea ? |
@trustin please review... we could also make use of Unsafe (if present) to replace Atomic_FieldUpdater as Unsafe is faster because it does not do any validation (which Atomic_FieldUpdater) does. |
Here they also tell about the performance difference: |
@daschl please also review |
Also about performance. With this branch I get slightly better performance (able to reproduce every time) , most likely because of the Atomic*FieldUpdater changes: This branch:
4.0 branch:
|
|
listeners.add(this); | ||
} | ||
}; | ||
new Thread(new Runnable() { |
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.
You could use GlobalEventExecutor
instead.
I wish it does not create a new entry when there's only one listener. |
Will see what I can do... Rest looks good beside your comments and ready to pull in after addressing them?
|
@trustin addressed all your comments. Please review again and let me know what you think now ;) |
@trustin ok to merge? |
This also remove some more synchronization and make heavy use of atomic operations now. The downside is that it creates more objects when FutureListeners are added. But this was the only performant way I could find to still make sure the order is correct while still not do heavy synchronization.
…r and AtomicReferenceFieldUpdater Make use of these in the new DefaultPromise but also in other areas.
Squashed into 2 commits |
boolean added = false; | ||
if (entry == null) { | ||
added = ENTRY_UPDATER.compareAndSet(this, null, newEntry); | ||
} |
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.
No need to wrap with if (entry == null) { }
because compareAndSet()
already does the same check?
PROGRESSIVE_SIZE_UPDATER.decrementAndGet(this); | ||
} | ||
break; | ||
} |
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 loop looks weird to me. The first if (entry == null || ...)
block and the second if (...) { if (...) {
block are pretty much same except that the second one decreases the counter, which means the second block will never be evaluated.
} | ||
} | ||
return this; | ||
entry = entry.next; |
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 loop is vulnerable to the race where more than on thread attempt to append a listener. When one thread succeeds to append a listener, the other thread can dereference it and thus we lose it.
- Also, you are traversing the linked list sequentially, which is potentially slow. Perhaps we could maintain the reference to the last listener?
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.
Yes... but this will make it even more hard to not use synchronized and I though it should not be a big problem. Maybe I could also just use synchronized and be happy ;)
How about this:
This basically uses executor's task queue for ordering. |
@trustin tried this and did not work out so far. Not sure if we are just trying to be too smart.. |
I will check again this evening about this issue.. |
#2186 shows an alternative fix |
Was fixed by 309ee68 |
This pull-req is not complete yet but show a race in DefaultPromise which can lead to have FutureListener notified in a wrong order.
One way to fix this is to enlarge the scope of the synchronization but I would like to think about some better way to fix it.
This can happen if addListener is called from a different thread then the EventExecutor that is used by the DefaultPromise itself and the DefaultPromise is notified while adding these listeners.