8186265: Make toString() methods of "task" objects more useful
Reviewed-by: martin, psandoz, rriggs, dholmes, darcy
This commit is contained in:
parent
2ea646cc20
commit
229cce5f44
@ -2490,13 +2490,13 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
|||||||
for (Completion p = stack; p != null; p = p.next)
|
for (Completion p = stack; p != null; p = p.next)
|
||||||
++count;
|
++count;
|
||||||
return super.toString() +
|
return super.toString() +
|
||||||
((r == null) ?
|
((r == null)
|
||||||
((count == 0) ?
|
? ((count == 0)
|
||||||
"[Not completed]" :
|
? "[Not completed]"
|
||||||
"[Not completed, " + count + " dependents]") :
|
: "[Not completed, " + count + " dependents]")
|
||||||
(((r instanceof AltResult) && ((AltResult)r).ex != null) ?
|
: (((r instanceof AltResult) && ((AltResult)r).ex != null)
|
||||||
"[Completed exceptionally]" :
|
? "[Completed exceptionally: " + ((AltResult)r).ex + "]"
|
||||||
"[Completed normally]"));
|
: "[Completed normally]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// jdk9 additions
|
// jdk9 additions
|
||||||
|
@ -514,6 +514,9 @@ public class Executors {
|
|||||||
task.run();
|
task.run();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + "[Wrapped task = " + task + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -540,6 +543,10 @@ public class Executors {
|
|||||||
throw e.getException();
|
throw e.getException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + "[Wrapped task = " + task + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -592,6 +599,10 @@ public class Executors {
|
|||||||
throw e.getException();
|
throw e.getException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + "[Wrapped task = " + task + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1375,6 +1375,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
|||||||
public final void setRawResult(T v) { result = v; }
|
public final void setRawResult(T v) { result = v; }
|
||||||
public final boolean exec() { runnable.run(); return true; }
|
public final boolean exec() { runnable.run(); return true; }
|
||||||
public final void run() { invoke(); }
|
public final void run() { invoke(); }
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + "[Wrapped task = " + runnable + "]";
|
||||||
|
}
|
||||||
private static final long serialVersionUID = 5232453952276885070L;
|
private static final long serialVersionUID = 5232453952276885070L;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1392,6 +1395,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
|||||||
public final void setRawResult(Void v) { }
|
public final void setRawResult(Void v) { }
|
||||||
public final boolean exec() { runnable.run(); return true; }
|
public final boolean exec() { runnable.run(); return true; }
|
||||||
public final void run() { invoke(); }
|
public final void run() { invoke(); }
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + "[Wrapped task = " + runnable + "]";
|
||||||
|
}
|
||||||
private static final long serialVersionUID = 5232453952276885070L;
|
private static final long serialVersionUID = 5232453952276885070L;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1437,6 +1443,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public final void run() { invoke(); }
|
public final void run() { invoke(); }
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + "[Wrapped task = " + callable + "]";
|
||||||
|
}
|
||||||
private static final long serialVersionUID = 2838392045355241008L;
|
private static final long serialVersionUID = 2838392045355241008L;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,6 +480,41 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of this FutureTask.
|
||||||
|
*
|
||||||
|
* @implSpec
|
||||||
|
* The default implementation returns a string identifying this
|
||||||
|
* FutureTask, as well as its completion state. The state, in
|
||||||
|
* brackets, contains one of the strings {@code "Completed Normally"},
|
||||||
|
* {@code "Completed Exceptionally"}, {@code "Cancelled"}, or {@code
|
||||||
|
* "Not completed"}.
|
||||||
|
*
|
||||||
|
* @return a string representation of this FutureTask
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
final String status;
|
||||||
|
switch (state) {
|
||||||
|
case NORMAL:
|
||||||
|
status = "[Completed normally]";
|
||||||
|
break;
|
||||||
|
case EXCEPTIONAL:
|
||||||
|
status = "[Completed exceptionally: " + outcome + "]";
|
||||||
|
break;
|
||||||
|
case CANCELLED:
|
||||||
|
case INTERRUPTING:
|
||||||
|
case INTERRUPTED:
|
||||||
|
status = "[Cancelled]";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
final Callable<?> callable = this.callable;
|
||||||
|
status = (callable == null)
|
||||||
|
? "[Not completed]"
|
||||||
|
: "[Not completed, task = " + callable + "]";
|
||||||
|
}
|
||||||
|
return super.toString() + status;
|
||||||
|
}
|
||||||
|
|
||||||
// VarHandle mechanics
|
// VarHandle mechanics
|
||||||
private static final VarHandle STATE;
|
private static final VarHandle STATE;
|
||||||
private static final VarHandle RUNNER;
|
private static final VarHandle RUNNER;
|
||||||
|
@ -74,7 +74,7 @@ public class Basic {
|
|||||||
check(!cf.isCompletedExceptionally(), "Expected isCompletedExceptionally to return false");
|
check(!cf.isCompletedExceptionally(), "Expected isCompletedExceptionally to return false");
|
||||||
check(!cf.isCancelled(), "Expected isCancelled to be false");
|
check(!cf.isCancelled(), "Expected isCancelled to be false");
|
||||||
check(!cf.cancel(true), "Expected cancel to return false");
|
check(!cf.cancel(true), "Expected cancel to return false");
|
||||||
check(cf.toString().contains("[Completed normally]"));
|
check(cf.toString().matches(".*\\[.*Completed normally.*\\]"));
|
||||||
check(cf.complete(null) == false, "Expected complete() to fail");
|
check(cf.complete(null) == false, "Expected complete() to fail");
|
||||||
check(cf.completeExceptionally(new Throwable()) == false,
|
check(cf.completeExceptionally(new Throwable()) == false,
|
||||||
"Expected completeExceptionally() to fail");
|
"Expected completeExceptionally() to fail");
|
||||||
@ -106,7 +106,7 @@ public class Basic {
|
|||||||
check(cf.isCompletedExceptionally(), "Expected isCompletedExceptionally");
|
check(cf.isCompletedExceptionally(), "Expected isCompletedExceptionally");
|
||||||
check(cf.isCancelled() == cancelled, "Expected isCancelled: " + cancelled + ", got:" + cf.isCancelled());
|
check(cf.isCancelled() == cancelled, "Expected isCancelled: " + cancelled + ", got:" + cf.isCancelled());
|
||||||
check(cf.cancel(true) == cancelled, "Expected cancel: " + cancelled + ", got:" + cf.cancel(true));
|
check(cf.cancel(true) == cancelled, "Expected cancel: " + cancelled + ", got:" + cf.cancel(true));
|
||||||
check(cf.toString().contains("[Completed exceptionally]")); // ## TODO: 'E'xceptionally
|
check(cf.toString().matches(".*\\[.*Completed exceptionally.*\\]")); // ## TODO: 'E'xceptionally
|
||||||
check(cf.complete((T)new Object()) == false, "Expected complete() to fail");
|
check(cf.complete((T)new Object()) == false, "Expected complete() to fail");
|
||||||
check(cf.completeExceptionally(new Throwable()) == false,
|
check(cf.completeExceptionally(new Throwable()) == false,
|
||||||
"Expected completeExceptionally() to fail, already completed");
|
"Expected completeExceptionally() to fail, already completed");
|
||||||
|
@ -86,7 +86,7 @@ public class CompletableFutureTest extends JSR166TestCase {
|
|||||||
void checkIncomplete(CompletableFuture<?> f) {
|
void checkIncomplete(CompletableFuture<?> f) {
|
||||||
assertFalse(f.isDone());
|
assertFalse(f.isDone());
|
||||||
assertFalse(f.isCancelled());
|
assertFalse(f.isCancelled());
|
||||||
assertTrue(f.toString().contains("Not completed"));
|
assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]"));
|
||||||
try {
|
try {
|
||||||
assertNull(f.getNow(null));
|
assertNull(f.getNow(null));
|
||||||
} catch (Throwable fail) { threadUnexpectedException(fail); }
|
} catch (Throwable fail) { threadUnexpectedException(fail); }
|
||||||
@ -109,7 +109,7 @@ public class CompletableFutureTest extends JSR166TestCase {
|
|||||||
assertTrue(f.isDone());
|
assertTrue(f.isDone());
|
||||||
assertFalse(f.isCancelled());
|
assertFalse(f.isCancelled());
|
||||||
assertFalse(f.isCompletedExceptionally());
|
assertFalse(f.isCompletedExceptionally());
|
||||||
assertTrue(f.toString().contains("[Completed normally]"));
|
assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -165,7 +165,7 @@ public class CompletableFutureTest extends JSR166TestCase {
|
|||||||
assertFalse(f.isCancelled());
|
assertFalse(f.isCancelled());
|
||||||
assertTrue(f.isDone());
|
assertTrue(f.isDone());
|
||||||
assertTrue(f.isCompletedExceptionally());
|
assertTrue(f.isCompletedExceptionally());
|
||||||
assertTrue(f.toString().contains("[Completed exceptionally]"));
|
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkCompletedWithWrappedCFException(CompletableFuture<?> f) {
|
void checkCompletedWithWrappedCFException(CompletableFuture<?> f) {
|
||||||
@ -220,7 +220,7 @@ public class CompletableFutureTest extends JSR166TestCase {
|
|||||||
assertTrue(f.isDone());
|
assertTrue(f.isDone());
|
||||||
assertTrue(f.isCompletedExceptionally());
|
assertTrue(f.isCompletedExceptionally());
|
||||||
assertTrue(f.isCancelled());
|
assertTrue(f.isCancelled());
|
||||||
assertTrue(f.toString().contains("[Completed exceptionally]"));
|
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -356,23 +356,40 @@ public class CompletableFutureTest extends JSR166TestCase {
|
|||||||
/**
|
/**
|
||||||
* toString indicates current completion state
|
* toString indicates current completion state
|
||||||
*/
|
*/
|
||||||
public void testToString() {
|
public void testToString_incomplete() {
|
||||||
CompletableFuture<String> f;
|
CompletableFuture<String> f = new CompletableFuture<String>();
|
||||||
|
assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]"));
|
||||||
f = new CompletableFuture<String>();
|
if (testImplementationDetails)
|
||||||
assertTrue(f.toString().contains("[Not completed]"));
|
assertEquals(identityString(f) + "[Not completed]",
|
||||||
|
f.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToString_normal() {
|
||||||
|
CompletableFuture<String> f = new CompletableFuture<String>();
|
||||||
assertTrue(f.complete("foo"));
|
assertTrue(f.complete("foo"));
|
||||||
assertTrue(f.toString().contains("[Completed normally]"));
|
assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]"));
|
||||||
|
if (testImplementationDetails)
|
||||||
|
assertEquals(identityString(f) + "[Completed normally]",
|
||||||
|
f.toString());
|
||||||
|
}
|
||||||
|
|
||||||
f = new CompletableFuture<String>();
|
public void testToString_exception() {
|
||||||
|
CompletableFuture<String> f = new CompletableFuture<String>();
|
||||||
assertTrue(f.completeExceptionally(new IndexOutOfBoundsException()));
|
assertTrue(f.completeExceptionally(new IndexOutOfBoundsException()));
|
||||||
assertTrue(f.toString().contains("[Completed exceptionally]"));
|
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]"));
|
||||||
|
if (testImplementationDetails)
|
||||||
|
assertTrue(f.toString().startsWith(
|
||||||
|
identityString(f) + "[Completed exceptionally: "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToString_cancelled() {
|
||||||
for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
|
for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
|
||||||
f = new CompletableFuture<String>();
|
CompletableFuture<String> f = new CompletableFuture<String>();
|
||||||
assertTrue(f.cancel(mayInterruptIfRunning));
|
assertTrue(f.cancel(mayInterruptIfRunning));
|
||||||
assertTrue(f.toString().contains("[Completed exceptionally]"));
|
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]"));
|
||||||
|
if (testImplementationDetails)
|
||||||
|
assertTrue(f.toString().startsWith(
|
||||||
|
identityString(f) + "[Completed exceptionally: "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,4 +613,56 @@ public class ExecutorsTest extends JSR166TestCase {
|
|||||||
} catch (NullPointerException success) {}
|
} catch (NullPointerException success) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* callable(runnable, x).toString() contains toString of wrapped task
|
||||||
|
*/
|
||||||
|
public void testCallable_withResult_toString() {
|
||||||
|
if (testImplementationDetails) {
|
||||||
|
Runnable r = () -> {};
|
||||||
|
Callable<String> c = Executors.callable(r, "");
|
||||||
|
assertEquals(
|
||||||
|
identityString(c) + "[Wrapped task = " + r.toString() + "]",
|
||||||
|
c.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* callable(runnable).toString() contains toString of wrapped task
|
||||||
|
*/
|
||||||
|
public void testCallable_toString() {
|
||||||
|
if (testImplementationDetails) {
|
||||||
|
Runnable r = () -> {};
|
||||||
|
Callable<Object> c = Executors.callable(r);
|
||||||
|
assertEquals(
|
||||||
|
identityString(c) + "[Wrapped task = " + r.toString() + "]",
|
||||||
|
c.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* privilegedCallable(callable).toString() contains toString of wrapped task
|
||||||
|
*/
|
||||||
|
public void testPrivilegedCallable_toString() {
|
||||||
|
if (testImplementationDetails) {
|
||||||
|
Callable<String> c = () -> "";
|
||||||
|
Callable<String> priv = Executors.privilegedCallable(c);
|
||||||
|
assertEquals(
|
||||||
|
identityString(priv) + "[Wrapped task = " + c.toString() + "]",
|
||||||
|
priv.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* privilegedCallableUsingCurrentClassLoader(callable).toString()
|
||||||
|
* contains toString of wrapped task
|
||||||
|
*/
|
||||||
|
public void testPrivilegedCallableUsingCurrentClassLoader_toString() {
|
||||||
|
if (testImplementationDetails) {
|
||||||
|
Callable<String> c = () -> "";
|
||||||
|
Callable<String> priv = Executors.privilegedCallableUsingCurrentClassLoader(c);
|
||||||
|
assertEquals(
|
||||||
|
identityString(priv) + "[Wrapped task = " + c.toString() + "]",
|
||||||
|
priv.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ForkJoinPool;
|
import java.util.concurrent.ForkJoinPool;
|
||||||
@ -1675,4 +1676,42 @@ public class ForkJoinTaskTest extends JSR166TestCase {
|
|||||||
testInvokeOnPool(mainPool(), a);
|
testInvokeOnPool(mainPool(), a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adapt(runnable).toString() contains toString of wrapped task
|
||||||
|
*/
|
||||||
|
public void testAdapt_Runnable_toString() {
|
||||||
|
if (testImplementationDetails) {
|
||||||
|
Runnable r = () -> {};
|
||||||
|
ForkJoinTask<?> task = ForkJoinTask.adapt(r);
|
||||||
|
assertEquals(
|
||||||
|
identityString(task) + "[Wrapped task = " + r.toString() + "]",
|
||||||
|
task.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adapt(runnable, x).toString() contains toString of wrapped task
|
||||||
|
*/
|
||||||
|
public void testAdapt_Runnable_withResult_toString() {
|
||||||
|
if (testImplementationDetails) {
|
||||||
|
Runnable r = () -> {};
|
||||||
|
ForkJoinTask<String> task = ForkJoinTask.adapt(r, "");
|
||||||
|
assertEquals(
|
||||||
|
identityString(task) + "[Wrapped task = " + r.toString() + "]",
|
||||||
|
task.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adapt(callable).toString() contains toString of wrapped task
|
||||||
|
*/
|
||||||
|
public void testAdapt_Callable_toString() {
|
||||||
|
if (testImplementationDetails) {
|
||||||
|
Callable<String> c = () -> "";
|
||||||
|
ForkJoinTask<String> task = ForkJoinTask.adapt(c);
|
||||||
|
assertEquals(
|
||||||
|
identityString(task) + "[Wrapped task = " + c.toString() + "]",
|
||||||
|
task.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -861,4 +861,45 @@ public class FutureTaskTest extends JSR166TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toString indicates current completion state
|
||||||
|
*/
|
||||||
|
public void testToString_incomplete() {
|
||||||
|
FutureTask<String> f = new FutureTask<String>(() -> "");
|
||||||
|
assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]"));
|
||||||
|
if (testImplementationDetails)
|
||||||
|
assertTrue(f.toString().startsWith(
|
||||||
|
identityString(f) + "[Not completed, task ="));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToString_normal() {
|
||||||
|
FutureTask<String> f = new FutureTask<String>(() -> "");
|
||||||
|
f.run();
|
||||||
|
assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]"));
|
||||||
|
if (testImplementationDetails)
|
||||||
|
assertEquals(identityString(f) + "[Completed normally]",
|
||||||
|
f.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToString_exception() {
|
||||||
|
FutureTask<String> f = new FutureTask<String>(
|
||||||
|
() -> { throw new ArithmeticException(); });
|
||||||
|
f.run();
|
||||||
|
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]"));
|
||||||
|
if (testImplementationDetails)
|
||||||
|
assertTrue(f.toString().startsWith(
|
||||||
|
identityString(f) + "[Completed exceptionally: "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToString_cancelled() {
|
||||||
|
for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
|
||||||
|
FutureTask<String> f = new FutureTask<String>(() -> "");
|
||||||
|
assertTrue(f.cancel(mayInterruptIfRunning));
|
||||||
|
assertTrue(f.toString().matches(".*\\[.*Cancelled.*\\]"));
|
||||||
|
if (testImplementationDetails)
|
||||||
|
assertEquals(identityString(f) + "[Cancelled]",
|
||||||
|
f.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -571,6 +571,7 @@ public class JSR166TestCase extends TestCase {
|
|||||||
"DoubleAdderTest",
|
"DoubleAdderTest",
|
||||||
"ForkJoinPool8Test",
|
"ForkJoinPool8Test",
|
||||||
"ForkJoinTask8Test",
|
"ForkJoinTask8Test",
|
||||||
|
"HashMapTest",
|
||||||
"LinkedBlockingDeque8Test",
|
"LinkedBlockingDeque8Test",
|
||||||
"LinkedBlockingQueue8Test",
|
"LinkedBlockingQueue8Test",
|
||||||
"LongAccumulatorTest",
|
"LongAccumulatorTest",
|
||||||
@ -1940,6 +1941,18 @@ public class JSR166TestCase extends TestCase {
|
|||||||
Collections.shuffle(Arrays.asList(array), ThreadLocalRandom.current());
|
Collections.shuffle(Arrays.asList(array), ThreadLocalRandom.current());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the same String as would be returned by {@link
|
||||||
|
* Object#toString}, whether or not the given object's class
|
||||||
|
* overrides toString().
|
||||||
|
*
|
||||||
|
* @see System#identityHashCode
|
||||||
|
*/
|
||||||
|
static String identityString(Object x) {
|
||||||
|
return x.getClass().getName()
|
||||||
|
+ "@" + Integer.toHexString(System.identityHashCode(x));
|
||||||
|
}
|
||||||
|
|
||||||
// --- Shared assertions for Executor tests ---
|
// --- Shared assertions for Executor tests ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user