Skip to content

Version 1.3.1 #1506

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

Merged
merged 21 commits into from
Sep 4, 2019
Merged

Version 1.3.1 #1506

merged 21 commits into from
Sep 4, 2019

Conversation

elizarov
Copy link
Contributor

@elizarov elizarov commented Sep 4, 2019

This is a minor update with various fixes:

JakeWharton and others added 21 commits August 23, 2019 00:34
It tests lock-freedom on send and receive operations on rendezvous
and conflated channels. The test is comprehensive enough to fail on
buffered channels (which are currently not lock-free) and will help us
ensuring that lock-freedom property is not lost while channels are
being improved and refactored.
Note that "receiveOrClosed" is internal now and is implemented only
partially to support flow, so this test is minimal.
* All stress tests end with StressTest, also:
* LFStressTest checks lock-freedom
* LCStressTest checks linearizability via LinCheck
* Gradle build exclusion/inclusion patterns are properly updated:
  - jdk16Test excludes both LFStressTest and LCStressTest since they
    depends on JDK8
  - Regular tests exclude LFStressTest since they are long (5 sec each)
  - A separate lockFreedomTest target that is run on "build"
* A dedicated test is added that checks consistency of unwrapping
  between slow and fast paths of both CompletionStage.await and
  CompletionStage.asDeferred implementations.
* In the fast-path for both of them implementation of
  CompletableFuture.get() does unwrapping of CompletionException, taking
  its cause when it is not null. To mimic this behavior, the slow path
  of both await and asDeferred shall perform similar unwrapping.

Fixes #1479
This fixes:
  - Cancellation without an untrapped CancellationException propagating
    through a Callback; isCancelled() is the correct way to check for
    cancellation
  - Bidirectional propagation of cancellation through
    `asListenableFuture()`
  - The cause getting lost in the `asListenableFuture()` future  when
    cancelling its `Deferred` parent with a cause

This also:
  - Extensively documents the package and the contracts created by the
    promise-creating extension methods and `future()`
  - Uses `getUninterruptibly()` for speed
  - Uses `AbstractFuture` to make as certain as possible that
    `Future.cancel()` will return `true` at most once
  - Should clear up rare spooky race conditions around
    cancellation/interruption in hybrid Coroutines/Guava Futures
    codebases

There are probably a few more interesting corner cases hiding in here,
but this should be a good start improving the correctness of `.guava`'s
adapters.

This is a squash commit of kotlin/pr/1347, rebased on develop:

- Incorporated first-round feedback.

- Merged CancellationToCoroutine into ListenableFutureCoroutine to save an
allocation.

- Documented and tested for null completion of asDeferred()'s parent
Future.

- Renamed a cancellation test case for clarity of purpose.

- Split asDeferred() documentation between KDoc/details

- Implemented InternalFutures faster-fast path. Documented.
* Add a failing test for a semaphore

This test consistently fails for the current implementation. It
attempts to cause the following state: after `job2` increments
`availablePermits` but before it wakes up the acquirer in the
queue, the acquirer is cancelled. Then, regardless of whether
`RESUMED` or `CANCELLED` was written first, another cell in the
queue is marked to be resumed. However, this is incorrect: on
cancellation, the acquirer incremented the number of available
permits once more, making it `1`; thus, at the same time there
exist two permits for acquiring the mutex. At the next loop
iteration, a new acquirer tries to claim ownership of the mutex and
succeeds because it goes to the thread queue and sees its cell as
`RESUMED`. Thus, two entities own a mutex at the same time.

* Fix a bug in semaphore implementation

The fix works as follows: if `availablePermits` is negative, its
absolute value denotes the logical length of the thread queue.
Increasing its value if it was negative means that this thread
promises to wake exactly one thread, and if its positive, returns
one permit to the semaphore itself.

Before, the error was in that a queue could be of negative length:
if it consisted of only `N` cells, and `N` resume queries arrived,
cancelling any threads would mean that there are more wakers then
there are sleepers, which breaks the invariants of the semaphore.
Thus, if on cancellation the acquirer detects that it leaves the
queue empty in the presence of resumers, it simply transfers the
semaphore acquisition permit to the semaphore itself, because it
knows that it, in a sense, owns it already: there is a thread that
is bound to resume this cell.
Also enable assertions and test output while running (long) LFStressTest
* Should not run long stress-tests during regular builds.
* Gradle metadata format version 1.0 (stable)
* Using Gradle publish task for publishing
* Empty javadoc files (only) are published
* Using atomicfu 0.13.0
@elizarov elizarov merged commit 762440a into master Sep 4, 2019
@elizarov elizarov deleted the version-1.3.1 branch September 4, 2019 14:52
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.

6 participants