Compare commits
3
Commits
baa01b22e3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d17f1e708 | ||
|
|
f99a8c87f0 | ||
|
|
7be5d0fe61 |
+40
-1
@@ -1341,7 +1341,6 @@ static void yield_process (hak_t* hak, hak_oop_process_t proc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int async_signal_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
|
static int async_signal_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
@@ -5640,6 +5639,11 @@ hak_pfrc_t hak_pf_process_resume (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
|
|||||||
return HAK_PF_FAILURE;
|
return HAK_PF_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* [SPECIAL CASE]
|
||||||
|
* resume_process changes the the active process.
|
||||||
|
* calling this after resume_process() pollutes a wrong stack. place it here */
|
||||||
|
HAK_STACK_SETRET(hak, nargs, prc);
|
||||||
|
|
||||||
resume_process(hak, prc);
|
resume_process(hak, prc);
|
||||||
return HAK_PF_SUCCESS;
|
return HAK_PF_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -5662,6 +5666,11 @@ hak_pfrc_t hak_pf_process_suspend (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
|
|||||||
prc = hak->processor->active;
|
prc = hak->processor->active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* [SPECIAL CASE]
|
||||||
|
* suspend_process changes the the active process.
|
||||||
|
* calling this after suspend_process() pollutes a wrong stack. place it here */
|
||||||
|
HAK_STACK_SETRET(hak, nargs, prc);
|
||||||
|
|
||||||
suspend_process(hak, prc);
|
suspend_process(hak, prc);
|
||||||
return HAK_PF_SUCCESS;
|
return HAK_PF_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -5684,18 +5693,33 @@ hak_pfrc_t hak_pf_process_terminate (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs
|
|||||||
prc = hak->processor->active;
|
prc = hak->processor->active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* [SPECIAL CASE]
|
||||||
|
* terminate_process changes the the active process.
|
||||||
|
* calling this after terminate_process() pollutes a wrong stack. place it here */
|
||||||
|
HAK_STACK_SETRET(hak, nargs, prc);
|
||||||
|
|
||||||
terminate_process(hak, prc);
|
terminate_process(hak, prc);
|
||||||
return HAK_PF_SUCCESS;
|
return HAK_PF_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
hak_pfrc_t hak_pf_process_terminate_all (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
|
hak_pfrc_t hak_pf_process_terminate_all (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
|
||||||
{
|
{
|
||||||
|
/* [SPECIAL CASE]
|
||||||
|
* terminate_all_processes changes the the active process.
|
||||||
|
* calling this after terminate_all_processes() pollutes a wrong stack. place it here */
|
||||||
|
HAK_STACK_SETRET(hak, nargs, hak->_nil);
|
||||||
|
|
||||||
terminate_all_processes(hak);
|
terminate_all_processes(hak);
|
||||||
return HAK_PF_SUCCESS;
|
return HAK_PF_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
hak_pfrc_t hak_pf_process_yield (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
|
hak_pfrc_t hak_pf_process_yield (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
|
||||||
{
|
{
|
||||||
|
/* [SPECIAL CASE]
|
||||||
|
* yield_process changes the the active process.
|
||||||
|
* calling this after yield_process() pollutes a wrong stack. place it here */
|
||||||
|
HAK_STACK_SETRET(hak, nargs, hak->_nil);
|
||||||
|
|
||||||
yield_process(hak, hak->processor->active);
|
yield_process(hak, hak->processor->active);
|
||||||
return HAK_PF_SUCCESS;
|
return HAK_PF_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -5715,7 +5739,22 @@ hak_pfrc_t hak_pf_semaphore_new (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
|
|||||||
return HAK_PF_FAILURE;
|
return HAK_PF_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nargs >= 1)
|
||||||
|
{
|
||||||
|
hak_oop_t tmp;
|
||||||
|
tmp = (hak_oop_semaphore_t)HAK_STACK_GETARG(hak, nargs, 0);
|
||||||
|
if (!HAK_OOP_IS_SMOOI(tmp))
|
||||||
|
{
|
||||||
|
hak_seterrbfmt(hak, HAK_EINVAL, "invalid semaphore count - %O", tmp);
|
||||||
|
return HAK_PF_FAILURE;
|
||||||
|
}
|
||||||
|
sem->count = tmp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
sem->count = HAK_SMOOI_TO_OOP(0);
|
sem->count = HAK_SMOOI_TO_OOP(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: sem->signal_action? */
|
/* TODO: sem->signal_action? */
|
||||||
/* other fields are all set to nil */
|
/* other fields are all set to nil */
|
||||||
|
|
||||||
|
|||||||
@@ -356,7 +356,7 @@
|
|||||||
* about async-signal-safety - the spinlock is built out of atomics and is safe
|
* about async-signal-safety - the spinlock is built out of atomics and is safe
|
||||||
* by that measure - but about reentrancy: a signal delivered to the thread
|
* by that measure - but about reentrancy: a signal delivered to the thread
|
||||||
* that already holds the lock would spin, or block, on a lock that thread can
|
* that already holds the lock would spin, or block, on a lock that thread can
|
||||||
* no longer reach the end of. post_sig_to_all_haks() therefore walk the chain without it.
|
* no longer reach the end of. post_sig_to_all_haks() therefore walks the chain without it.
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
#if defined(USE_THREAD)
|
#if defined(USE_THREAD)
|
||||||
@@ -2104,6 +2104,8 @@ kqueue_syserr:
|
|||||||
#elif defined(USE_SELECT)
|
#elif defined(USE_SELECT)
|
||||||
# define MUXEVT_FD(e) ((e).fd)
|
# define MUXEVT_FD(e) ((e).fd)
|
||||||
# define MUXEVT_MASK(e) ((e).events)
|
# define MUXEVT_MASK(e) ((e).events)
|
||||||
|
#else
|
||||||
|
# error UNSUPPORTED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Drop multiplexer events already sitting in the buffer for this descriptor.
|
/* Drop multiplexer events already sitting in the buffer for this descriptor.
|
||||||
@@ -2453,20 +2455,7 @@ static void vm_muxwait (hak_t* hak, const hak_ntime_t* dur, hak_vmprim_muxwait_c
|
|||||||
{
|
{
|
||||||
--n;
|
--n;
|
||||||
|
|
||||||
#if defined(USE_DEVPOLL)
|
if (MUXEVT_FD(xtn->ev.buf[n]) == xtn->iothr.p[0])
|
||||||
if (xtn->ev.buf[n].fd == xtn->iothr.p[0])
|
|
||||||
#elif defined(USE_KQUEUE)
|
|
||||||
if (xtn->ev.buf[n].ident == xtn->iothr.p[0])
|
|
||||||
#elif defined(USE_EPOLL)
|
|
||||||
/*if (xtn->ev.buf[n].data.ptr == (void*)HAK_TYPE_MAX(hak_oow_t))*/
|
|
||||||
if (xtn->ev.buf[n].data.fd == xtn->iothr.p[0])
|
|
||||||
#elif defined(USE_POLL)
|
|
||||||
if (xtn->ev.buf[n].fd == xtn->iothr.p[0])
|
|
||||||
#elif defined(USE_SELECT)
|
|
||||||
if (xtn->ev.buf[n].fd == xtn->iothr.p[0])
|
|
||||||
#else
|
|
||||||
# error UNSUPPORTED
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
hak_uint8_t u8;
|
hak_uint8_t u8;
|
||||||
while (read(xtn->iothr.p[0], &u8, HAK_SIZEOF(u8)) > 0)
|
while (read(xtn->iothr.p[0], &u8, HAK_SIZEOF(u8)) > 0)
|
||||||
@@ -2480,21 +2469,15 @@ static void vm_muxwait (hak_t* hak, const hak_ntime_t* dur, hak_vmprim_muxwait_c
|
|||||||
int revents;
|
int revents;
|
||||||
hak_ooi_t mask;
|
hak_ooi_t mask;
|
||||||
|
|
||||||
#if defined(USE_DEVPOLL)
|
#if defined(USE_KQUEUE)
|
||||||
revents = xtn->ev.buf[n].revents;
|
|
||||||
#elif defined(USE_KQUEUE)
|
|
||||||
revents = 0;
|
revents = 0;
|
||||||
/* it's "if .. else if" because kqueue filter is either READ or WRITE. */
|
/* it's "if .. else if" because kqueue filter is either READ or WRITE. */
|
||||||
if (xtn->ev.buf[n].filter == EVFILT_READ) revents |= XPOLLIN;
|
if (xtn->ev.buf[n].filter == EVFILT_READ) revents |= XPOLLIN;
|
||||||
else if (xtn->ev.buf[n].filter == EVFILT_WRITE) revents |= XPOLLOUT;
|
else if (xtn->ev.buf[n].filter == EVFILT_WRITE) revents |= XPOLLOUT;
|
||||||
if (xtn->ev.buf[n].flags & EV_EOF) revents |= XPOLLHUP;
|
if (xtn->ev.buf[n].flags & EV_EOF) revents |= XPOLLHUP;
|
||||||
if (xtn->ev.buf[n].flags & EV_ERROR) revents |= XPOLLERR;
|
if (xtn->ev.buf[n].flags & EV_ERROR) revents |= XPOLLERR;
|
||||||
#elif defined(USE_EPOLL)
|
#else
|
||||||
revents = xtn->ev.buf[n].events;
|
revents = MUXEVT_MASK(xtn->ev.buf[n]);
|
||||||
#elif defined(USE_POLL)
|
|
||||||
revents = xtn->ev.buf[n].revents;
|
|
||||||
#elif defined(USE_SELECT)
|
|
||||||
revents = xtn->ev.buf[n].events;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mask = 0;
|
mask = 0;
|
||||||
@@ -2503,19 +2486,7 @@ static void vm_muxwait (hak_t* hak, const hak_ntime_t* dur, hak_vmprim_muxwait_c
|
|||||||
if (revents & XPOLLERR) mask |= HAK_SEMAPHORE_IO_MASK_ERROR;
|
if (revents & XPOLLERR) mask |= HAK_SEMAPHORE_IO_MASK_ERROR;
|
||||||
if (revents & XPOLLHUP) mask |= HAK_SEMAPHORE_IO_MASK_HANGUP;
|
if (revents & XPOLLHUP) mask |= HAK_SEMAPHORE_IO_MASK_HANGUP;
|
||||||
|
|
||||||
#if defined(USE_DEVPOLL)
|
muxwcb(hak, MUXEVT_FD(xtn->ev.buf[n]), mask);
|
||||||
muxwcb(hak, xtn->ev.buf[n].fd, mask);
|
|
||||||
#elif defined(USE_KQUEUE)
|
|
||||||
muxwcb(hak, xtn->ev.buf[n].ident, mask);
|
|
||||||
#elif defined(USE_EPOLL)
|
|
||||||
muxwcb(hak, xtn->ev.buf[n].data.fd, mask);
|
|
||||||
#elif defined(USE_POLL)
|
|
||||||
muxwcb(hak, xtn->ev.buf[n].fd, mask);
|
|
||||||
#elif defined(USE_SELECT)
|
|
||||||
muxwcb(hak, xtn->ev.buf[n].fd, mask);
|
|
||||||
#else
|
|
||||||
# error UNSUPPORTED
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (n > 0);
|
while (n > 0);
|
||||||
@@ -2864,6 +2835,7 @@ static void dispatch_siginfo (int sig, siginfo_t* si, void* ctx)
|
|||||||
if (g_sig_state[sig].handler != (hak_uintptr_t)SIG_IGN &&
|
if (g_sig_state[sig].handler != (hak_uintptr_t)SIG_IGN &&
|
||||||
g_sig_state[sig].handler != (hak_uintptr_t)SIG_DFL)
|
g_sig_state[sig].handler != (hak_uintptr_t)SIG_DFL)
|
||||||
{
|
{
|
||||||
|
/* execute the current handler */
|
||||||
((sig_handler_t)g_sig_state[sig].handler)(sig);
|
((sig_handler_t)g_sig_state[sig].handler)(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2871,6 +2843,9 @@ static void dispatch_siginfo (int sig, siginfo_t* si, void* ctx)
|
|||||||
g_sig_state[sig].old_handler != (hak_uintptr_t)SIG_IGN &&
|
g_sig_state[sig].old_handler != (hak_uintptr_t)SIG_IGN &&
|
||||||
g_sig_state[sig].old_handler != (hak_uintptr_t)SIG_DFL)
|
g_sig_state[sig].old_handler != (hak_uintptr_t)SIG_DFL)
|
||||||
{
|
{
|
||||||
|
/* execute the original remembered handler */
|
||||||
|
/* TODO: if the runtime has installed its own signal handler, proably this one must not be called.
|
||||||
|
* when the runtime registers a single handler, it may optionally request that the previous one should also be invoked? */
|
||||||
((void(*)(int, siginfo_t*, void*))g_sig_state[sig].old_handler)(sig, si, ctx);
|
((void(*)(int, siginfo_t*, void*))g_sig_state[sig].old_handler)(sig, si, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3137,8 +3112,10 @@ static HAK_INLINE void post_sig_to_all_haks (int signo)
|
|||||||
{
|
{
|
||||||
xtn_t* xtn = GET_XTN(hak);
|
xtn_t* xtn = GET_XTN(hak);
|
||||||
hak_uint8_t u8;
|
hak_uint8_t u8;
|
||||||
/*hak_abortstd(hak);*/
|
|
||||||
u8 = signo & 0xFF;
|
u8 = signo & 0xFF;
|
||||||
|
/* write a byte of signal number. vm_getsig() reads this when
|
||||||
|
* it's invoked by the vm */
|
||||||
write(xtn->sigfd.p[1], &u8, HAK_SIZEOF(u8));
|
write(xtn->sigfd.p[1], &u8, HAK_SIZEOF(u8));
|
||||||
hak = xtn->next;
|
hak = xtn->next;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -606,6 +606,7 @@ static hak_pfinfo_t pfinfos[] =
|
|||||||
{ "cons", { HAK_PFBASE_FUNC, pf_core_cons, 2, 2 } },
|
{ "cons", { HAK_PFBASE_FUNC, pf_core_cons, 2, 2 } },
|
||||||
|
|
||||||
{ "current-process", { HAK_PFBASE_FUNC, hak_pf_process_current, 0, 0 } },
|
{ "current-process", { HAK_PFBASE_FUNC, hak_pf_process_current, 0, 0 } },
|
||||||
|
|
||||||
{ "eqk?", { HAK_PFBASE_FUNC, hak_pf_eqk, 2, 2 } },
|
{ "eqk?", { HAK_PFBASE_FUNC, hak_pf_eqk, 2, 2 } },
|
||||||
{ "eql?", { HAK_PFBASE_FUNC, hak_pf_eql, 2, 2 } },
|
{ "eql?", { HAK_PFBASE_FUNC, hak_pf_eql, 2, 2 } },
|
||||||
{ "eqv?", { HAK_PFBASE_FUNC, hak_pf_eqv, 2, 2 } },
|
{ "eqv?", { HAK_PFBASE_FUNC, hak_pf_eqv, 2, 2 } },
|
||||||
@@ -621,8 +622,10 @@ static hak_pfinfo_t pfinfos[] =
|
|||||||
|
|
||||||
{ "primAt", { HAK_PFBASE_FUNC, pf_core_prim_at, 2, 2 } },
|
{ "primAt", { HAK_PFBASE_FUNC, pf_core_prim_at, 2, 2 } },
|
||||||
{ "primAtPut", { HAK_PFBASE_FUNC, pf_core_prim_at_put, 3, 3 } },
|
{ "primAtPut", { HAK_PFBASE_FUNC, pf_core_prim_at_put, 3, 3 } },
|
||||||
|
|
||||||
{ "resume", { HAK_PFBASE_FUNC, hak_pf_process_resume, 1, 1 } },
|
{ "resume", { HAK_PFBASE_FUNC, hak_pf_process_resume, 1, 1 } },
|
||||||
{ "sem-new", { HAK_PFBASE_FUNC, hak_pf_semaphore_new, 0, 0 } },
|
|
||||||
|
{ "sem-new", { HAK_PFBASE_FUNC, hak_pf_semaphore_new, 0, 1 } },
|
||||||
{ "sem-signal", { HAK_PFBASE_FUNC, hak_pf_semaphore_signal, 1, 3 } },
|
{ "sem-signal", { HAK_PFBASE_FUNC, hak_pf_semaphore_signal, 1, 3 } },
|
||||||
{ "sem-signal-on-input", { HAK_PFBASE_FUNC, hak_pf_semaphore_signal_on_input, 2, 2 } },
|
{ "sem-signal-on-input", { HAK_PFBASE_FUNC, hak_pf_semaphore_signal_on_input, 2, 2 } },
|
||||||
{ "sem-signal-on-output", { HAK_PFBASE_FUNC, hak_pf_semaphore_signal_on_output, 2, 2 } },
|
{ "sem-signal-on-output", { HAK_PFBASE_FUNC, hak_pf_semaphore_signal_on_output, 2, 2 } },
|
||||||
@@ -632,13 +635,16 @@ static hak_pfinfo_t pfinfos[] =
|
|||||||
{ "semgr-new", { HAK_PFBASE_FUNC, hak_pf_semaphore_group_new, 0, 0 } },
|
{ "semgr-new", { HAK_PFBASE_FUNC, hak_pf_semaphore_group_new, 0, 0 } },
|
||||||
{ "semgr-remove", { HAK_PFBASE_FUNC, hak_pf_semaphore_group_remove_semaphore, 1, 2 } },
|
{ "semgr-remove", { HAK_PFBASE_FUNC, hak_pf_semaphore_group_remove_semaphore, 1, 2 } },
|
||||||
{ "semgr-wait", { HAK_PFBASE_FUNC, hak_pf_semaphore_group_wait, 1, 1 } },
|
{ "semgr-wait", { HAK_PFBASE_FUNC, hak_pf_semaphore_group_wait, 1, 1 } },
|
||||||
|
|
||||||
{ "slice", { HAK_PFBASE_FUNC, pf_core_slice, 3, 3 } },
|
{ "slice", { HAK_PFBASE_FUNC, pf_core_slice, 3, 3 } },
|
||||||
{ "smooiToChar", { HAK_PFBASE_FUNC, pf_core_smooi_to_char, 1, 1 } },
|
{ "smooiToChar", { HAK_PFBASE_FUNC, pf_core_smooi_to_char, 1, 1 } },
|
||||||
{ "sqrt", { HAK_PFBASE_FUNC, hak_pf_number_sqrt, 1, 1 } },
|
{ "sqrt", { HAK_PFBASE_FUNC, hak_pf_number_sqrt, 1, 1 } },
|
||||||
|
|
||||||
{ "suspend", { HAK_PFBASE_FUNC, hak_pf_process_suspend, 0, 1 } },
|
{ "suspend", { HAK_PFBASE_FUNC, hak_pf_process_suspend, 0, 1 } },
|
||||||
{ "terminate", { HAK_PFBASE_FUNC, hak_pf_process_terminate, 0, 1 } },
|
{ "terminate", { HAK_PFBASE_FUNC, hak_pf_process_terminate, 0, 1 } },
|
||||||
{ "terminate-all", { HAK_PFBASE_FUNC, hak_pf_process_terminate_all, 0, 0 } },
|
{ "terminate-all", { HAK_PFBASE_FUNC, hak_pf_process_terminate_all, 0, 0 } },
|
||||||
{ "yield", { HAK_PFBASE_FUNC, hak_pf_process_yield, 0, 0 } },
|
{ "yield", { HAK_PFBASE_FUNC, hak_pf_process_yield, 0, 0 } },
|
||||||
|
|
||||||
{ "~=", { HAK_PFBASE_FUNC, hak_pf_number_ne, 2, 2 } },
|
{ "~=", { HAK_PFBASE_FUNC, hak_pf_number_ne, 2, 2 } },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+17
-9
@@ -13,27 +13,35 @@ class[#uncopyable] Semaphore: Object(
|
|||||||
_grm_next
|
_grm_next
|
||||||
) {
|
) {
|
||||||
fun[#class] new() {
|
fun[#class] new() {
|
||||||
return (core.sem-new)
|
return (core.sem-new 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun[#class] forMutex() {
|
||||||
|
return (core.sem-new 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun signal() {
|
||||||
|
return (core.sem-signal self 0 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun signalAfter(secs nsecs) {
|
fun signalAfter(secs nsecs) {
|
||||||
core.sem-signal self secs nsecs
|
return (core.sem-signal self secs nsecs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun signalOnInput(handle) {
|
fun signalOnInput(handle) {
|
||||||
core.sem-signal-on-input self handle
|
return (core.sem-signal-on-input self handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun signalOnOutput(handle) {
|
fun signalOnOutput(handle) {
|
||||||
core.sem-signal-on-output self handle
|
return (core.sem-signal-on-output self handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unsignal() {
|
fun unsignal() {
|
||||||
core.sem-unsignal self
|
return (core.sem-unsignal self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun wait() {
|
fun wait() {
|
||||||
core.sem-wait self
|
return (core.sem-wait self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,14 +60,14 @@ class[#uncopyable] SemaphoreGroup: Object(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun add(sem) {
|
fun add(sem) {
|
||||||
core.semgr-add self sem
|
return (core.semgr-add self sem)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun remove(sem) {
|
fun remove(sem) {
|
||||||
core.semgr-remove self sem
|
return (core.semgr-remove self sem)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun wait() {
|
fun wait() {
|
||||||
core.semgr-wait self
|
return (core.semgr-wait self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user