Skip to content

Commit

Permalink
Work around an android bug in LockSupport, this time with @CanIgnoreR…
Browse files Browse the repository at this point in the history
…eturnValue

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=327861568
  • Loading branch information
clm authored and netdpb committed Aug 24, 2020
1 parent dbc87a9 commit 9b972a2
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ public V get(long timeout, TimeUnit unit)
node.setNext(oldHead);
if (ATOMIC_HELPER.casWaiters(this, oldHead, node)) {
while (true) {
LockSupport.parkNanos(this, remainingNanos);
OverflowAvoidingLockSupport.parkNanos(this, remainingNanos);
// Check interruption first, if we woke up due to interruption we need to honor that.
if (Thread.interrupted()) {
removeWaiter(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@

package com.google.common.util.concurrent;

import static java.lang.Math.min;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

import com.google.common.annotations.GwtIncompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
Expand Down Expand Up @@ -80,6 +87,20 @@ public void addListener(Runnable listener, Executor exec) {
executionList.add(listener, exec);
}

@CanIgnoreReturnValue
@Override
public V get(long timeout, TimeUnit unit)
throws TimeoutException, InterruptedException, ExecutionException {

long timeoutNanos = unit.toNanos(timeout);
if (timeoutNanos <= OverflowAvoidingLockSupport.MAX_NANOSECONDS_THRESHOLD) {
return super.get(timeout, unit);
}
// Waiting 68 years should be enough for any program.
return super.get(
min(timeoutNanos, OverflowAvoidingLockSupport.MAX_NANOSECONDS_THRESHOLD), NANOSECONDS);
}

/** Internal implementation detail used to invoke the listeners. */
@Override
protected void done() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2020 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.common.util.concurrent;

import static java.lang.Math.min;

import java.util.concurrent.locks.LockSupport;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
* Works around an android bug, where parking for more than INT_MAX seconds can produce an abort
* signal on 32 bit devices running Android Q.
*/
final class OverflowAvoidingLockSupport {
// Represents the max nanoseconds representable on a linux timespec with a 32 bit tv_sec
static final long MAX_NANOSECONDS_THRESHOLD = (1L + Integer.MAX_VALUE) * 1_000_000_000L - 1L;

private OverflowAvoidingLockSupport() {}

static void parkNanos(@NullableDecl Object blocker, long nanos) {
// Even in the extremely unlikely event that a thread unblocks itself early after only 68 years,
// this is indistinguishable from a spurious wakeup, which LockSupport allows.
LockSupport.parkNanos(blocker, min(nanos, MAX_NANOSECONDS_THRESHOLD));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ public V get(long timeout, TimeUnit unit)
node.setNext(oldHead);
if (ATOMIC_HELPER.casWaiters(this, oldHead, node)) {
while (true) {
LockSupport.parkNanos(this, remainingNanos);
OverflowAvoidingLockSupport.parkNanos(this, remainingNanos);
// Check interruption first, if we woke up due to interruption we need to honor that.
if (Thread.interrupted()) {
removeWaiter(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@

package com.google.common.util.concurrent;

import static java.lang.Math.min;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

import com.google.common.annotations.GwtIncompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
Expand Down Expand Up @@ -80,6 +87,20 @@ public void addListener(Runnable listener, Executor exec) {
executionList.add(listener, exec);
}

@CanIgnoreReturnValue
@Override
public V get(long timeout, TimeUnit unit)
throws TimeoutException, InterruptedException, ExecutionException {

long timeoutNanos = unit.toNanos(timeout);
if (timeoutNanos <= OverflowAvoidingLockSupport.MAX_NANOSECONDS_THRESHOLD) {
return super.get(timeout, unit);
}
// Waiting 68 years should be enough for any program.
return super.get(
min(timeoutNanos, OverflowAvoidingLockSupport.MAX_NANOSECONDS_THRESHOLD), NANOSECONDS);
}

/** Internal implementation detail used to invoke the listeners. */
@Override
protected void done() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2020 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.common.util.concurrent;

import static java.lang.Math.min;

import java.util.concurrent.locks.LockSupport;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Works around an android bug, where parking for more than INT_MAX seconds can produce an abort
* signal on 32 bit devices running Android Q.
*/
final class OverflowAvoidingLockSupport {
// Represents the max nanoseconds representable on a linux timespec with a 32 bit tv_sec
static final long MAX_NANOSECONDS_THRESHOLD = (1L + Integer.MAX_VALUE) * 1_000_000_000L - 1L;

private OverflowAvoidingLockSupport() {}

static void parkNanos(@Nullable Object blocker, long nanos) {
// Even in the extremely unlikely event that a thread unblocks itself early after only 68 years,
// this is indistinguishable from a spurious wakeup, which LockSupport allows.
LockSupport.parkNanos(blocker, min(nanos, MAX_NANOSECONDS_THRESHOLD));
}
}

0 comments on commit 9b972a2

Please sign in to comment.