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

ConcurrentSubscripiton avoid concurrent access for invalid demand #1015

Merged
merged 9 commits into from May 5, 2020

Conversation

Scottmitch
Copy link
Member

Motivation:
ConcurrentSubscription currently propagates invalid demand without any
concurrency protection. In general this is invalid use of the API but
may invalidate underlying data structures that are not thread safe and
result in undefined results.

Modifications:

  • Use a simpler locking scheme inspired by Publisher#flatMapMerge design
    which allows for re-entry and also notification is another thread has
    attempted to acquire the lock which will trigger re-processing.

Result:
ConcurrentSubscripiton no longer allows any concurrent access and uses a
more common/shareable locking utility.

private static long mapInvalidRequestN(long n) {
// We map zero to a negative number because zero could later be overwritten by a subsequent legit value of
// n, and we want to ensure the invalid use gets propagated.
return n == CANCELLED ? CANCELLED + 1 : n == 0 ? -1 : n;
Copy link
Member

Choose a reason for hiding this comment

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

nit: Imho chaining stuff like this is really not readable. Consider using a branch

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

I prefer to keep the code as is as it is more compact and I don't think readability is dramatically different between the two options:

current option:

return n == CANCELLED ? CANCELLED + 1 : n == 0 ? -1 : n;

suggested option:

if (n == CANCELLED) {
  return CANCELLED + 1;
}
return n == 0 ? -1 : n;

@Scottmitch Scottmitch force-pushed the concurrent_subscription branch 2 times, most recently from 825e5c2 to 71b94a4 Compare April 28, 2020 04:37
@Scottmitch
Copy link
Member Author

humm ... ci failures

Creating servicetalk-java8-prb_runtime-setup_1 ... done
Error: No such container: f7df93647ccd8b7580a740e917771dbc1589b9f79d8694758bcf73a244e776d8
21:45:17 No such container: f7df93647ccd8b7580a740e917771dbc1589b9f79d8694758bcf73a244e776d8
21:45:17 Build step 'Execute shell' marked build as failure

@Scottmitch
Copy link
Member Author

@servicetalk-bot - test this please

@Scottmitch
Copy link
Member Author

build failure attributed to #744

@Scottmitch
Copy link
Member Author

@servicetalk-bot - test this please

@Scottmitch
Copy link
Member Author

ci failure...

08:28:08 
Creating servicetalk-java8-prb_runtime-setup_1 ... done
time="2020-04-28T15:28:09Z" level=error msg="error waiting for container: context canceled"
08:28:09 Error response from daemon: failed to get network during CreateEndpoint: network 82cd76a80b00834668362278f1efff2e358505fff034522ffee45181c158dfe9 not found
08:28:09 Build step 'Execute shell' marked build as failure

@Scottmitch
Copy link
Member Author

@servicetalk-bot - test this please

Copy link
Collaborator

@NiteshKant NiteshKant left a comment

Choose a reason for hiding this comment

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

Nice simplification of ConcurrentSubscription 💯

@Scottmitch
Copy link
Member Author

suspected CI related failure ... https://ci.servicetalk.io/job/servicetalk-java11-prb/1280/consoleText

BUILD SUCCESSFUL in 9m 56s
301 actionable tasks: 242 executed, 59 up-to-date
removal of container ad39f54ec9fb47d45e167e146414ef1ca48efa7f3bc34ca711a307b6f548bfa3 is already in progress
Build step 'Execute shell' marked build as failure

@Scottmitch
Copy link
Member Author

@servicetalk-bot - test this please

@Scottmitch
Copy link
Member Author

Scottmitch commented Apr 30, 2020

Benchmarks were revised to provide multi-threaded perf for non-reentrant case and more stack depth options for recursion.

Summary:

tryAcquireReentrantLock

  • gains against master for reentrant (+151% to +500% depending upon recursion depth)
  • gains against tryAcquireLock for reentrant (bigger margins than master)
  • gains against master for single threaded non-reentrant (+28%)
  • similar to master for multi threaded non-reentrant
  • similar to tryAcquireLock for non-reentrant

tryAcquireLock

  • regresses against master for reentrant (-10% to -30% depending upon recursion depth)
## master
Benchmark                                              (stackDepth)   Mode  Cnt         Score        Error  Units
ConcurrentSubscriptionReentrantBenchmark.singleThread             2  thrpt    5  24318761.311 ± 965534.969  ops/s
ConcurrentSubscriptionReentrantBenchmark.singleThread            10  thrpt    5   4608999.521 ± 475365.160  ops/s
ConcurrentSubscriptionReentrantBenchmark.singleThread           100  thrpt    5    453085.063 ±  53186.602  ops/s

Benchmark                                      Mode  Cnt         Score        Error  Units
ConcurrentSubscriptionBenchmark.multiThread   thrpt    5   6753766.730 ± 477061.905  ops/s
ConcurrentSubscriptionBenchmark.singleThread  thrpt    5  58855485.836 ± 715964.377  ops/s

## Reentrant (using tryAcquireReentrantLock)
Benchmark                                              (stackDepth)   Mode  Cnt         Score        Error  Units
ConcurrentSubscriptionReentrantBenchmark.singleThread             2  thrpt    5  61086581.693 ± 866190.998  ops/s
ConcurrentSubscriptionReentrantBenchmark.singleThread            10  thrpt    5  27976426.295 ± 842739.171  ops/s
ConcurrentSubscriptionReentrantBenchmark.singleThread           100  thrpt    5   2248121.486 ±  54539.770  ops/s

Benchmark                                      Mode  Cnt         Score         Error  Units
ConcurrentSubscriptionBenchmark.multiThread   thrpt    5   6491110.907 ±  317372.027  ops/s
ConcurrentSubscriptionBenchmark.singleThread  thrpt    5  76085499.345 ± 1123039.174  ops/s

## Non-reentrant (using tryAcquireLock)
Benchmark                                              (stackDepth)   Mode  Cnt         Score         Error  Units
ConcurrentSubscriptionReentrantBenchmark.singleThread             2  thrpt    5  21694566.056 ± 2973401.571  ops/s
ConcurrentSubscriptionReentrantBenchmark.singleThread            10  thrpt    5   3410420.825 ±  384962.783  ops/s
ConcurrentSubscriptionReentrantBenchmark.singleThread           100  thrpt    5    323562.643 ±   37266.246  ops/s

Benchmark                                      Mode  Cnt         Score         Error  Units
ConcurrentSubscriptionBenchmark.multiThread   thrpt    5   6423213.603 ±  601263.682  ops/s
ConcurrentSubscriptionBenchmark.singleThread  thrpt    5  75500536.822 ± 5127380.129  ops/s

Copy link
Collaborator

@NiteshKant NiteshKant left a comment

Choose a reason for hiding this comment

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

Final comments then LGTM

@Scottmitch
Copy link
Member Author

build failure attributed to #1040

Motivation:
ConcurrentSubscription currently propagates invalid demand without any
concurrency protection. In general this is invalid use of the API but
may invalidate underlying data structures that are not thread safe and
result in undefined results.

Modifications:
- Use a simpler locking scheme inspired by Publisher#flatMapMerge design
which allows for re-entry and also notification is another thread has
attempted to acquire the lock which will trigger re-processing.

Result:
ConcurrentSubscripiton no longer allows any concurrent access and uses a
more common/shareable locking utility.
@Scottmitch
Copy link
Member Author

discussed relaxing the exception handling with @NiteshKant offline. I've modified this PR to incorporate @NiteshKant's suggestions and we can enhance later if necessary.

Copy link
Collaborator

@NiteshKant NiteshKant left a comment

Choose a reason for hiding this comment

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

💯

@Scottmitch Scottmitch merged commit e57a01c into apple:master May 5, 2020
@Scottmitch Scottmitch deleted the concurrent_subscription branch May 5, 2020 01:40
NiteshKant pushed a commit to NiteshKant/servicetalk that referenced this pull request May 6, 2020
…ple#1015)

Motivation:
ConcurrentSubscription currently propagates invalid demand without any
concurrency protection. In general this is invalid use of the API but
may invalidate underlying data structures that are not thread safe and
result in undefined results.

Modifications:
- Use a simpler locking scheme inspired by Publisher#flatMapMerge design
which allows for re-entry and also notification is another thread has
attempted to acquire the lock which will trigger re-processing.

Result:
ConcurrentSubscripiton no longer allows any concurrent access and uses a
more common/shareable locking utility.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants