Move idle notification handling to GCIdleTimeHandler.
BUG= R=hpayer@chromium.org Review URL: https://codereview.chromium.org/492763002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23282 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
758dd167f7
commit
53fdf75be1
@ -3,12 +3,14 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/heap/gc-idle-time-handler.h"
|
||||
#include "src/heap/gc-tracer.h"
|
||||
#include "src/utils.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
|
||||
const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9;
|
||||
const size_t GCIdleTimeHandler::kMaxMarkCompactTimeInMs = 1000000;
|
||||
|
||||
|
||||
size_t GCIdleTimeHandler::EstimateMarkingStepSize(
|
||||
@ -16,14 +18,13 @@ size_t GCIdleTimeHandler::EstimateMarkingStepSize(
|
||||
DCHECK(idle_time_in_ms > 0);
|
||||
|
||||
if (marking_speed_in_bytes_per_ms == 0) {
|
||||
marking_speed_in_bytes_per_ms =
|
||||
GCIdleTimeHandler::kInitialConservativeMarkingSpeed;
|
||||
marking_speed_in_bytes_per_ms = kInitialConservativeMarkingSpeed;
|
||||
}
|
||||
|
||||
size_t marking_step_size = marking_speed_in_bytes_per_ms * idle_time_in_ms;
|
||||
if (marking_step_size / marking_speed_in_bytes_per_ms != idle_time_in_ms) {
|
||||
// In the case of an overflow we return maximum marking step size.
|
||||
return GCIdleTimeHandler::kMaximumMarkingStepSize;
|
||||
return kMaximumMarkingStepSize;
|
||||
}
|
||||
|
||||
if (marking_step_size > kMaximumMarkingStepSize)
|
||||
@ -32,5 +33,51 @@ size_t GCIdleTimeHandler::EstimateMarkingStepSize(
|
||||
return static_cast<size_t>(marking_step_size *
|
||||
GCIdleTimeHandler::kConservativeTimeRatio);
|
||||
}
|
||||
|
||||
|
||||
size_t GCIdleTimeHandler::EstimateMarkCompactTime(
|
||||
size_t size_of_objects, size_t mark_compact_speed_in_bytes_per_ms) {
|
||||
if (mark_compact_speed_in_bytes_per_ms == 0) {
|
||||
mark_compact_speed_in_bytes_per_ms = kInitialConservativeMarkCompactSpeed;
|
||||
}
|
||||
size_t result = size_of_objects / mark_compact_speed_in_bytes_per_ms;
|
||||
return Min(result, kMaxMarkCompactTimeInMs);
|
||||
}
|
||||
|
||||
|
||||
GCIdleTimeAction GCIdleTimeHandler::Compute(int idle_time_in_ms,
|
||||
int contexts_disposed,
|
||||
size_t size_of_objects,
|
||||
bool incremental_marking_stopped,
|
||||
GCTracer* gc_tracer) {
|
||||
if (IsIdleRoundFinished()) {
|
||||
if (EnoughGarbageSinceLastIdleRound() || contexts_disposed > 0) {
|
||||
StartIdleRound();
|
||||
} else {
|
||||
return GCIdleTimeAction::Nothing();
|
||||
}
|
||||
}
|
||||
if (incremental_marking_stopped) {
|
||||
size_t speed =
|
||||
static_cast<size_t>(gc_tracer->MarkCompactSpeedInBytesPerMillisecond());
|
||||
if (idle_time_in_ms >=
|
||||
static_cast<int>(EstimateMarkCompactTime(size_of_objects, speed))) {
|
||||
// If there are no more than two GCs left in this idle round and we are
|
||||
// allowed to do a full GC, then make those GCs full in order to compact
|
||||
// the code space.
|
||||
// TODO(ulan): Once we enable code compaction for incremental marking, we
|
||||
// can get rid of this special case and always start incremental marking.
|
||||
int remaining_mark_sweeps =
|
||||
kMaxMarkCompactsInIdleRound - mark_compacts_since_idle_round_started_;
|
||||
if (contexts_disposed > 0 || remaining_mark_sweeps <= 2) {
|
||||
return GCIdleTimeAction::FullGC();
|
||||
}
|
||||
}
|
||||
}
|
||||
intptr_t speed = gc_tracer->IncrementalMarkingSpeedInBytesPerMillisecond();
|
||||
size_t step_size =
|
||||
static_cast<size_t>(EstimateMarkingStepSize(idle_time_in_ms, speed));
|
||||
return GCIdleTimeAction::IncrementalMarking(step_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,13 +10,51 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
enum GCIdleTimeActionType {
|
||||
DO_NOTHING,
|
||||
DO_INCREMENTAL_MARKING,
|
||||
DO_SCAVENGE,
|
||||
DO_FULL_GC
|
||||
};
|
||||
|
||||
|
||||
class GCIdleTimeAction {
|
||||
public:
|
||||
static GCIdleTimeAction Nothing() {
|
||||
GCIdleTimeAction result;
|
||||
result.type = DO_NOTHING;
|
||||
result.parameter = 0;
|
||||
return result;
|
||||
}
|
||||
static GCIdleTimeAction IncrementalMarking(intptr_t step_size) {
|
||||
GCIdleTimeAction result;
|
||||
result.type = DO_INCREMENTAL_MARKING;
|
||||
result.parameter = step_size;
|
||||
return result;
|
||||
}
|
||||
static GCIdleTimeAction Scavenge() {
|
||||
GCIdleTimeAction result;
|
||||
result.type = DO_SCAVENGE;
|
||||
result.parameter = 0;
|
||||
return result;
|
||||
}
|
||||
static GCIdleTimeAction FullGC() {
|
||||
GCIdleTimeAction result;
|
||||
result.type = DO_FULL_GC;
|
||||
result.parameter = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
GCIdleTimeActionType type;
|
||||
intptr_t parameter;
|
||||
};
|
||||
|
||||
class GCTracer;
|
||||
|
||||
// The idle time handler makes decisions about which garbage collection
|
||||
// operations are executing during IdleNotification.
|
||||
class GCIdleTimeHandler {
|
||||
public:
|
||||
static size_t EstimateMarkingStepSize(size_t idle_time_in_ms,
|
||||
size_t marking_speed_in_bytes_per_ms);
|
||||
|
||||
// If we haven't recorded any incremental marking events yet, we carefully
|
||||
// mark with a conservative lower bound for the marking speed.
|
||||
static const size_t kInitialConservativeMarkingSpeed = 100 * KB;
|
||||
@ -28,7 +66,55 @@ class GCIdleTimeHandler {
|
||||
// idle_time_in_ms. Hence, we conservatively prune our workload estimate.
|
||||
static const double kConservativeTimeRatio;
|
||||
|
||||
// If we haven't recorded any mark-compact events yet, we use
|
||||
// conservative lower bound for the mark-compact speed.
|
||||
static const size_t kInitialConservativeMarkCompactSpeed = 2 * MB;
|
||||
|
||||
// Maximum mark-compact time returned by EstimateMarkCompactTime.
|
||||
static const size_t kMaxMarkCompactTimeInMs;
|
||||
|
||||
GCIdleTimeHandler()
|
||||
: mark_compacts_since_idle_round_started_(0),
|
||||
scavenges_since_last_idle_round_(0) {}
|
||||
|
||||
GCIdleTimeAction Compute(int idle_time_in_ms, int contexts_disposed,
|
||||
size_t size_of_objects,
|
||||
bool incremental_marking_stopped,
|
||||
GCTracer* gc_tracer);
|
||||
|
||||
void NotifyIdleMarkCompact() {
|
||||
if (mark_compacts_since_idle_round_started_ < kMaxMarkCompactsInIdleRound) {
|
||||
++mark_compacts_since_idle_round_started_;
|
||||
if (mark_compacts_since_idle_round_started_ ==
|
||||
kMaxMarkCompactsInIdleRound) {
|
||||
scavenges_since_last_idle_round_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyScavenge() { ++scavenges_since_last_idle_round_; }
|
||||
|
||||
static size_t EstimateMarkingStepSize(size_t idle_time_in_ms,
|
||||
size_t marking_speed_in_bytes_per_ms);
|
||||
|
||||
static size_t EstimateMarkCompactTime(
|
||||
size_t size_of_objects, size_t mark_compact_speed_in_bytes_per_ms);
|
||||
|
||||
private:
|
||||
void StartIdleRound() { mark_compacts_since_idle_round_started_ = 0; }
|
||||
bool IsIdleRoundFinished() {
|
||||
return mark_compacts_since_idle_round_started_ ==
|
||||
kMaxMarkCompactsInIdleRound;
|
||||
}
|
||||
bool EnoughGarbageSinceLastIdleRound() {
|
||||
return scavenges_since_last_idle_round_ >= kIdleScavengeThreshold;
|
||||
}
|
||||
|
||||
static const int kMaxMarkCompactsInIdleRound = 7;
|
||||
static const int kIdleScavengeThreshold = 5;
|
||||
int mark_compacts_since_idle_round_started_;
|
||||
int scavenges_since_last_idle_round_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GCIdleTimeHandler);
|
||||
};
|
||||
|
||||
|
@ -418,5 +418,22 @@ intptr_t GCTracer::ScavengeSpeedInBytesPerMillisecond() const {
|
||||
|
||||
return static_cast<intptr_t>(bytes / durations);
|
||||
}
|
||||
|
||||
|
||||
intptr_t GCTracer::MarkCompactSpeedInBytesPerMillisecond() const {
|
||||
intptr_t bytes = 0;
|
||||
double durations = 0.0;
|
||||
EventBuffer::const_iterator iter = mark_compactor_events_.begin();
|
||||
while (iter != mark_compactor_events_.end()) {
|
||||
bytes += iter->start_object_size;
|
||||
durations += iter->end_time - iter->start_time +
|
||||
iter->pure_incremental_marking_duration;
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (durations == 0.0) return 0;
|
||||
|
||||
return static_cast<intptr_t>(bytes / durations);
|
||||
}
|
||||
}
|
||||
} // namespace v8::internal
|
||||
|
@ -5,6 +5,8 @@
|
||||
#ifndef V8_HEAP_GC_TRACER_H_
|
||||
#define V8_HEAP_GC_TRACER_H_
|
||||
|
||||
#include "src/base/platform/platform.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
@ -81,9 +83,9 @@ class RingBuffer {
|
||||
// GCTracer collects and prints ONE line after each garbage collector
|
||||
// invocation IFF --trace_gc is used.
|
||||
// TODO(ernstm): Unit tests.
|
||||
class GCTracer BASE_EMBEDDED {
|
||||
class GCTracer {
|
||||
public:
|
||||
class Scope BASE_EMBEDDED {
|
||||
class Scope {
|
||||
public:
|
||||
enum ScopeId {
|
||||
EXTERNAL,
|
||||
@ -291,6 +293,10 @@ class GCTracer BASE_EMBEDDED {
|
||||
// Returns 0 if no events have been recorded.
|
||||
intptr_t ScavengeSpeedInBytesPerMillisecond() const;
|
||||
|
||||
// Compute the max mark-sweep speed in bytes/millisecond.
|
||||
// Returns 0 if no events have been recorded.
|
||||
intptr_t MarkCompactSpeedInBytesPerMillisecond() const;
|
||||
|
||||
private:
|
||||
// Print one detailed trace line in name=value format.
|
||||
// TODO(ernstm): Move to Heap.
|
||||
|
106
src/heap/heap.cc
106
src/heap/heap.cc
@ -120,12 +120,7 @@ Heap::Heap()
|
||||
store_buffer_(this),
|
||||
marking_(this),
|
||||
incremental_marking_(this),
|
||||
number_idle_notifications_(0),
|
||||
last_idle_notification_gc_count_(0),
|
||||
last_idle_notification_gc_count_init_(false),
|
||||
mark_sweeps_since_idle_round_started_(0),
|
||||
gc_count_at_last_idle_gc_(0),
|
||||
scavenges_since_last_idle_round_(kIdleScavengeThreshold),
|
||||
full_codegen_bytes_generated_(0),
|
||||
crankshaft_codegen_bytes_generated_(0),
|
||||
gcs_since_last_deopt_(0),
|
||||
@ -1559,7 +1554,7 @@ void Heap::Scavenge() {
|
||||
|
||||
gc_state_ = NOT_IN_GC;
|
||||
|
||||
scavenges_since_last_idle_round_++;
|
||||
gc_idle_time_handler_.NotifyScavenge();
|
||||
}
|
||||
|
||||
|
||||
@ -4265,12 +4260,7 @@ void Heap::MakeHeapIterable() {
|
||||
}
|
||||
|
||||
|
||||
void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) {
|
||||
intptr_t step_size =
|
||||
static_cast<size_t>(GCIdleTimeHandler::EstimateMarkingStepSize(
|
||||
idle_time_in_ms,
|
||||
tracer_.IncrementalMarkingSpeedInBytesPerMillisecond()));
|
||||
|
||||
void Heap::AdvanceIdleIncrementalMarking(intptr_t step_size) {
|
||||
incremental_marking()->Step(step_size,
|
||||
IncrementalMarking::NO_GC_VIA_STACK_GUARD, true);
|
||||
|
||||
@ -4283,7 +4273,7 @@ void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) {
|
||||
}
|
||||
CollectAllGarbage(kReduceMemoryFootprintMask,
|
||||
"idle notification: finalize incremental");
|
||||
mark_sweeps_since_idle_round_started_++;
|
||||
gc_idle_time_handler_.NotifyIdleMarkCompact();
|
||||
gc_count_at_last_idle_gc_ = gc_count_;
|
||||
if (uncommit) {
|
||||
new_space_.Shrink();
|
||||
@ -4296,83 +4286,49 @@ void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) {
|
||||
bool Heap::IdleNotification(int idle_time_in_ms) {
|
||||
// If incremental marking is off, we do not perform idle notification.
|
||||
if (!FLAG_incremental_marking) return true;
|
||||
|
||||
// Minimal hint that allows to do full GC.
|
||||
const int kMinHintForFullGC = 100;
|
||||
isolate()->counters()->gc_idle_time_allotted_in_ms()->AddSample(
|
||||
idle_time_in_ms);
|
||||
HistogramTimerScope idle_notification_scope(
|
||||
isolate_->counters()->gc_idle_notification());
|
||||
|
||||
if (contexts_disposed_ > 0) {
|
||||
contexts_disposed_ = 0;
|
||||
int mark_sweep_time = Min(TimeMarkSweepWouldTakeInMs(), 1000);
|
||||
if (idle_time_in_ms >= mark_sweep_time && !FLAG_expose_gc &&
|
||||
incremental_marking()->IsStopped()) {
|
||||
GCIdleTimeAction action = gc_idle_time_handler_.Compute(
|
||||
idle_time_in_ms, contexts_disposed_, static_cast<size_t>(SizeOfObjects()),
|
||||
incremental_marking()->IsStopped(), tracer());
|
||||
contexts_disposed_ = 0;
|
||||
bool result = false;
|
||||
switch (action.type) {
|
||||
case DO_INCREMENTAL_MARKING:
|
||||
if (incremental_marking()->IsStopped()) {
|
||||
incremental_marking()->Start();
|
||||
}
|
||||
AdvanceIdleIncrementalMarking(action.parameter);
|
||||
break;
|
||||
case DO_FULL_GC: {
|
||||
HistogramTimerScope scope(isolate_->counters()->gc_context());
|
||||
CollectAllGarbage(kReduceMemoryFootprintMask,
|
||||
"idle notification: contexts disposed");
|
||||
} else {
|
||||
AdvanceIdleIncrementalMarking(idle_time_in_ms);
|
||||
const char* message = contexts_disposed_
|
||||
? "idle notification: contexts disposed"
|
||||
: "idle notification: finalize idle round";
|
||||
CollectAllGarbage(kReduceMemoryFootprintMask, message);
|
||||
gc_idle_time_handler_.NotifyIdleMarkCompact();
|
||||
break;
|
||||
}
|
||||
|
||||
// After context disposal there is likely a lot of garbage remaining, reset
|
||||
// the idle notification counters in order to trigger more incremental GCs
|
||||
// on subsequent idle notifications.
|
||||
StartIdleRound();
|
||||
return false;
|
||||
case DO_SCAVENGE:
|
||||
CollectGarbage(NEW_SPACE, "idle notification: scavenge");
|
||||
break;
|
||||
case DO_NOTHING:
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// By doing small chunks of GC work in each IdleNotification,
|
||||
// perform a round of incremental GCs and after that wait until
|
||||
// the mutator creates enough garbage to justify a new round.
|
||||
// An incremental GC progresses as follows:
|
||||
// 1. many incremental marking steps,
|
||||
// 2. one old space mark-sweep-compact,
|
||||
// Use mark-sweep-compact events to count incremental GCs in a round.
|
||||
|
||||
if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
|
||||
if (EnoughGarbageSinceLastIdleRound()) {
|
||||
StartIdleRound();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int remaining_mark_sweeps =
|
||||
kMaxMarkSweepsInIdleRound - mark_sweeps_since_idle_round_started_;
|
||||
|
||||
if (incremental_marking()->IsStopped()) {
|
||||
// If there are no more than two GCs left in this idle round and we are
|
||||
// allowed to do a full GC, then make those GCs full in order to compact
|
||||
// the code space.
|
||||
// TODO(ulan): Once we enable code compaction for incremental marking,
|
||||
// we can get rid of this special case and always start incremental marking.
|
||||
if (remaining_mark_sweeps <= 2 && idle_time_in_ms >= kMinHintForFullGC) {
|
||||
CollectAllGarbage(kReduceMemoryFootprintMask,
|
||||
"idle notification: finalize idle round");
|
||||
mark_sweeps_since_idle_round_started_++;
|
||||
} else {
|
||||
incremental_marking()->Start();
|
||||
}
|
||||
}
|
||||
if (!incremental_marking()->IsStopped()) {
|
||||
AdvanceIdleIncrementalMarking(idle_time_in_ms);
|
||||
}
|
||||
|
||||
if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
|
||||
FinishIdleRound();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the IdleNotifcation is called with a large hint we will wait for
|
||||
// the sweepter threads here.
|
||||
// TODO(ulan): move this in GCIdleTimeHandler.
|
||||
const int kMinHintForFullGC = 100;
|
||||
if (idle_time_in_ms >= kMinHintForFullGC &&
|
||||
mark_compact_collector()->sweeping_in_progress()) {
|
||||
mark_compact_collector()->EnsureSweepingCompleted();
|
||||
}
|
||||
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "src/assert-scope.h"
|
||||
#include "src/counters.h"
|
||||
#include "src/globals.h"
|
||||
#include "src/heap/gc-idle-time-handler.h"
|
||||
#include "src/heap/gc-tracer.h"
|
||||
#include "src/heap/incremental-marking.h"
|
||||
#include "src/heap/mark-compact.h"
|
||||
@ -1929,30 +1930,7 @@ class Heap {
|
||||
|
||||
void SelectScavengingVisitorsTable();
|
||||
|
||||
void StartIdleRound() { mark_sweeps_since_idle_round_started_ = 0; }
|
||||
|
||||
void FinishIdleRound() {
|
||||
mark_sweeps_since_idle_round_started_ = kMaxMarkSweepsInIdleRound;
|
||||
scavenges_since_last_idle_round_ = 0;
|
||||
}
|
||||
|
||||
bool EnoughGarbageSinceLastIdleRound() {
|
||||
return (scavenges_since_last_idle_round_ >= kIdleScavengeThreshold);
|
||||
}
|
||||
|
||||
// Estimates how many milliseconds a Mark-Sweep would take to complete.
|
||||
// In idle notification handler we assume that this function will return:
|
||||
// - a number less than 10 for small heaps, which are less than 8Mb.
|
||||
// - a number greater than 10 for large heaps, which are greater than 32Mb.
|
||||
int TimeMarkSweepWouldTakeInMs() {
|
||||
// Rough estimate of how many megabytes of heap can be processed in 1 ms.
|
||||
static const int kMbPerMs = 2;
|
||||
|
||||
int heap_size_mb = static_cast<int>(SizeOfObjects() / MB);
|
||||
return heap_size_mb / kMbPerMs;
|
||||
}
|
||||
|
||||
void AdvanceIdleIncrementalMarking(int idle_time_in_ms);
|
||||
void AdvanceIdleIncrementalMarking(intptr_t step_size);
|
||||
|
||||
void ClearObjectStats(bool clear_last_time_stats = false);
|
||||
|
||||
@ -2005,13 +1983,8 @@ class Heap {
|
||||
|
||||
IncrementalMarking incremental_marking_;
|
||||
|
||||
int number_idle_notifications_;
|
||||
unsigned int last_idle_notification_gc_count_;
|
||||
bool last_idle_notification_gc_count_init_;
|
||||
|
||||
int mark_sweeps_since_idle_round_started_;
|
||||
GCIdleTimeHandler gc_idle_time_handler_;
|
||||
unsigned int gc_count_at_last_idle_gc_;
|
||||
int scavenges_since_last_idle_round_;
|
||||
|
||||
// These two counters are monotomically increasing and never reset.
|
||||
size_t full_codegen_bytes_generated_;
|
||||
@ -2029,7 +2002,7 @@ class Heap {
|
||||
static const int kAllocationSiteScratchpadSize = 256;
|
||||
int allocation_sites_scratchpad_length_;
|
||||
|
||||
static const int kMaxMarkSweepsInIdleRound = 7;
|
||||
static const int kMaxMarkCompactsInIdleRound = 7;
|
||||
static const int kIdleScavengeThreshold = 5;
|
||||
|
||||
// Shared state read by the scavenge collector and set by ScavengeObject.
|
||||
|
@ -45,5 +45,29 @@ TEST(EstimateMarkingStepSizeTest, EstimateMarkingStepSizeOverflow2) {
|
||||
step_size);
|
||||
}
|
||||
|
||||
|
||||
TEST(EstimateMarkCompactTimeTest, EstimateMarkCompactTimeInitial) {
|
||||
size_t size = 100 * MB;
|
||||
size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, 0);
|
||||
EXPECT_EQ(size / GCIdleTimeHandler::kInitialConservativeMarkCompactSpeed,
|
||||
time);
|
||||
}
|
||||
|
||||
|
||||
TEST(EstimateMarkCompactTimeTest, EstimateMarkCompactTimeNonZero) {
|
||||
size_t size = 100 * MB;
|
||||
size_t speed = 10 * KB;
|
||||
size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, speed);
|
||||
EXPECT_EQ(size / speed, time);
|
||||
}
|
||||
|
||||
|
||||
TEST(EstimateMarkCompactTimeTest, EstimateMarkCompactTimeMax) {
|
||||
size_t size = std::numeric_limits<size_t>::max();
|
||||
size_t speed = 1;
|
||||
size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, speed);
|
||||
EXPECT_EQ(GCIdleTimeHandler::kMaxMarkCompactTimeInMs, time);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user