bool "POSIX signals (EXPERIMENTAL)"
select LIBPOSIX_PROCESS_PIDS
select LIBSYSCALL_SHIM
+ select LIBUKLOCK
config LIBPOSIX_PROCESS_CLONE
bool "clone() system call"
LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/process.c
LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/wait.c
LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signals.c
+
+LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_SIGNAL) += $(LIBPOSIX_PROCESS_BASE)/signal/signal.c
+
LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/clone.c
LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/clonetab.ld
LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/arch/$(CONFIG_UK_ARCH)/clone.c|$(CONFIG_UK_ARCH)
#include <uk/process.h>
#endif /* CONFIG_LIBPOSIX_PROCESS_CLONE */
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+#include "signal/signal.h"
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+
#include "process.h"
/**
pthread->process = pprocess;
pthread->tid = tid;
pthread->thread = th;
- uk_list_add_tail(&pthread->thread_list_entry, &pprocess->threads);
+
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+ err = pprocess_signal_tdesc_alloc(pthread);
+ if (unlikely(err)) {
+ uk_pr_err("Could not allocate signal descriptor\n");
+ /* Handle rollback manually to avoid adding more
+ * signal conditionals.
+ */
+ uk_free(a, pthread);
+ goto err_free_tid;
+ }
+ err = pprocess_signal_tdesc_init(pthread);
+ if (unlikely(err)) {
+ uk_pr_err("Could not initialize signal descriptor\n");
+ /* Handle rollback manually to avoid adding more
+ * signal conditionals.
+ */
+ pprocess_signal_tdesc_free(pthread);
+ uk_free(a, pthread);
+ goto err_free_tid;
+ }
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
/* Store reference to pthread with TID */
tid_thread[tid] = pthread;
+ /* Add to parent's list of threads */
+ uk_list_add_tail(&pthread->thread_list_entry, &pprocess->threads);
+
uk_pr_debug("Process PID %d: New thread TID %d\n",
(int) pprocess->pid, (int) pthread->tid);
return pthread;
UK_ASSERT(pthread);
UK_ASSERT(pthread->_a);
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+ pprocess_signal_tdesc_free(pthread);
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+
/* remove from process' thread list */
uk_list_del_init(&pthread->thread_list_entry);
UK_INIT_LIST_HEAD(&pprocess->threads);
UK_INIT_LIST_HEAD(&pprocess->children);
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+ ret = pprocess_signal_pdesc_alloc(pprocess);
+ if (unlikely(ret)) {
+ uk_pr_err("Could not allocate signal descriptor\n");
+ /* Free manually as we can jump to err_free_pprocess
+ * after this allocation is successful.
+ */
+ uk_free(a, pprocess);
+ goto err_out;
+ }
+ ret = pprocess_signal_pdesc_init(pprocess);
+ if (unlikely(ret)) {
+ uk_pr_err("Could not initialize signal descriptor\n");
+ goto err_free_pprocess;
+ }
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+
/* Check if we have a pthread structure already for this thread
* or if we need to allocate one
*/
uk_list_add_tail(&(*pthread)->thread_list_entry,
&pprocess->threads);
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+ /* Reset signal state of this thread */
+ ret = pprocess_signal_tdesc_init(*pthread);
+ if (unlikely(ret)) {
+ uk_pr_err("Could not initialize signal descriptor\n");
+ goto err_free_pprocess;
+ }
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+
/* Release original process if it became empty of threads */
if (uk_list_empty(&orig_pprocess->threads))
pprocess_release(orig_pprocess);
}
- /* Add to process table */
+ /* Add to process table. No failure past this point. */
if (unlikely((unsigned long)pprocess->pid >= ARRAY_SIZE(pid_process))) {
uk_pr_err("Process limit reached, could not create new process\n");
ret = -EAGAIN;
return 0;
err_free_pprocess:
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+ pprocess_signal_pdesc_free(pprocess);
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
uk_free(a, pprocess);
err_out:
return ret;
}
}
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+ pprocess_signal_pdesc_free(pprocess);
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+
pid_process[pprocess->pid] = NULL;
uk_pr_debug("Process PID %d released\n",
struct uk_list_head child_list_entry;
struct uk_list_head threads;
struct uk_alloc *_a;
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+ struct uk_signal_pdesc *signal;
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
/* TODO: Mutex */
};
struct uk_thread *thread;
struct uk_alloc *_a;
enum posix_thread_state state;
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+ struct uk_signal_tdesc *signal;
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
/* TODO: Mutex */
};
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ */
+
+#include "../process.h"
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ */
+
+#include <uk/process.h>
+#include <uk/refcount.h>
+#include <uk/semaphore.h>
+
+#include "process.h"
+#include "sigset.h"
+#include "signal.h"
+
+/* Enqueue signal for a thread or a process
+ *
+ * Setting pthread to NULL signifies that the signal
+ * targets the process.
+ */
+static int pprocess_signal_enqueue(struct posix_process *pproc,
+ struct posix_thread *pthread,
+ struct uk_signal *sig)
+{
+ int signum;
+
+ UK_ASSERT(sig);
+ UK_ASSERT(pproc);
+
+ if (pthread)
+ UK_ASSERT(pthread->process == pproc);
+
+ if (unlikely(pproc->signal->queued_count >= pproc->signal->queued_max))
+ return -EAGAIN;
+
+ signum = sig->siginfo.si_signo;
+
+ if (pthread) {
+ /* Standard signals can be queued once */
+ if (signum < SIGRTMIN && IS_PENDING(pthread->signal->sigqueue, signum))
+ return 0;
+
+ uk_pr_debug("Queueing signal %d for tid %d (pid %d)\n",
+ signum, pthread->tid, pproc->pid);
+
+ uk_list_add_tail(&sig->list_head,
+ &pthread->signal->sigqueue.list_head[signum]);
+ SET_PENDING(pproc->signal->sigqueue, signum);
+ } else {
+ /* Standard signals can be queued once */
+ if (signum < SIGRTMIN && IS_PENDING(pproc->signal->sigqueue, signum))
+ return 0;
+
+ uk_pr_debug("Queueing signal %d for pid %d\n",
+ signum, pproc->pid);
+
+ uk_list_add_tail(&sig->list_head,
+ &pproc->signal->sigqueue.list_head[signum]);
+ SET_PENDING(pproc->signal->sigqueue, signum);
+ }
+
+ pproc->signal->queued_count++;
+
+ return 0;
+}
+
+struct uk_signal *pprocess_signal_dequeue(struct posix_process *pproc,
+ struct posix_thread *pthread,
+ int signum)
+{
+ struct uk_signal_queue *sigqueue;
+ struct uk_signal *sig;
+
+ UK_ASSERT(pproc);
+
+ if (pthread) {
+ UK_ASSERT(pthread->process == pproc);
+ sigqueue = &pthread->signal->sigqueue;
+ } else {
+ sigqueue = &pproc->signal->sigqueue;
+ }
+
+ sig = uk_list_first_entry_or_null(&sigqueue->list_head[signum],
+ struct uk_signal,
+ list_head);
+ if (!sig)
+ return sig;
+
+ uk_list_del(&sig->list_head);
+
+ /* Reset pending bit if the signal queue is empty */
+ if (uk_list_empty(&sigqueue->list_head[signum])) {
+ if (pthread)
+ RESET_PENDING(pthread->signal->sigqueue, signum);
+ else
+ RESET_PENDING(pproc->signal->sigqueue, signum);
+ }
+
+ pproc->signal->queued_count--;
+
+ return sig;
+}
+
+struct uk_signal *pprocess_signal_next_pending_t(struct posix_thread *pthread)
+{
+ struct posix_process *pproc;
+ struct uk_signal *sig;
+ int signum;
+
+ pproc = tid2pprocess(pthread->tid);
+ UK_ASSERT(pproc);
+
+ pprocess_signal_foreach(signum) {
+ if (!pprocess_signal_is_deliverable(pthread, signum))
+ continue;
+
+ sig = pprocess_signal_dequeue(pproc, pthread, signum);
+ if (sig)
+ return sig;
+ }
+
+ return NULL;
+}
+
+void pprocess_signal_clear_pending(struct posix_process *proc, int signum)
+{
+ struct posix_thread *thread, *threadn;
+ struct uk_signal *sig;
+
+ if (IS_PENDING(proc->signal->sigqueue, signum)) {
+ while ((sig = pprocess_signal_dequeue(proc, NULL, signum)))
+ uk_signal_free(proc->_a, sig);
+ }
+
+ uk_pprocess_foreach_pthread(proc, thread, threadn) {
+ if (!IS_PENDING(thread->signal->sigqueue, signum))
+ continue;
+ while ((sig = pprocess_signal_dequeue(proc, thread, signum)))
+ uk_signal_free(thread->_a, sig);
+ }
+}
+
+void pprocess_signal_sigaction_clear(struct kern_sigaction *ks)
+{
+ ks->ks_handler = SIG_DFL;
+ ks->ks_flags = 0;
+ ks->ks_restorer = NULL;
+ uk_sigemptyset(&ks->ks_mask);
+}
+
+int pprocess_signal_sigaction_new(struct uk_alloc *alloc, struct uk_signal_pdesc *pd)
+{
+ pd->sigaction = uk_malloc(alloc, sizeof(struct uk_sigaction));
+ if (unlikely(!pd->sigaction)) {
+ uk_pr_err("Could not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ pd->sigaction->alloc = alloc;
+ uk_refcount_init(&pd->sigaction->refcnt, 1);
+
+ return 0;
+}
+
+int pprocess_signal_pdesc_alloc(struct posix_process *process)
+{
+ UK_ASSERT(process);
+
+ process->signal = uk_malloc(process->_a,
+ sizeof(struct uk_signal_pdesc));
+ if (unlikely(!process->signal)) {
+ uk_pr_err("Could not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int pprocess_signal_tdesc_alloc(struct posix_thread *thread)
+{
+ UK_ASSERT(thread);
+
+ thread->signal = uk_malloc(thread->_a, sizeof(struct uk_signal_tdesc));
+ if (unlikely(!thread->signal)) {
+ uk_pr_err("Could not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int pprocess_signal_pdesc_init(struct posix_process *process)
+{
+ struct uk_signal_pdesc *pd;
+ int signum;
+ int rc;
+
+ UK_ASSERT(process);
+ UK_ASSERT(process->signal);
+
+ pd = process->signal;
+
+ pd->queued_count = 0;
+ pd->queued_max = _POSIX_SIGQUEUE_MAX;
+
+ uk_sigemptyset(&pd->sigqueue.pending);
+
+ pprocess_signal_foreach(signum)
+ UK_INIT_LIST_HEAD(&pd->sigqueue.list_head[signum]);
+
+ rc = pprocess_signal_sigaction_new(process->_a, pd);
+ if (unlikely(rc))
+ return rc;
+
+ pprocess_signal_foreach(signum)
+ pprocess_signal_sigaction_clear(KERN_SIGACTION(process, signum));
+
+ return 0;
+}
+
+int pprocess_signal_tdesc_init(struct posix_thread *thread)
+{
+ struct uk_signal_tdesc *td;
+ int signum;
+
+ UK_ASSERT(thread);
+ UK_ASSERT(thread->signal);
+
+ td = thread->signal;
+
+ uk_sigemptyset(&td->mask);
+ uk_sigemptyset(&td->sigqueue.pending);
+
+ pprocess_signal_foreach(signum)
+ UK_INIT_LIST_HEAD(&td->sigqueue.list_head[signum]);
+
+ uk_semaphore_init(&thread->signal->pending_semaphore, 0);
+ uk_semaphore_init(&thread->signal->deliver_semaphore, 0);
+
+ return 0;
+}
+
+void pprocess_signal_pdesc_free(struct posix_process *process)
+{
+ struct uk_signal *sig;
+ int signum;
+
+ UK_ASSERT(process);
+ UK_ASSERT(process->signal);
+
+ pprocess_signal_foreach(signum)
+ while ((sig = pprocess_signal_dequeue(process, NULL, signum)))
+ uk_signal_free(process->_a, sig);
+
+ pprocess_signal_sigaction_release(process->signal->sigaction);
+
+ uk_free(process->_a, process->signal);
+}
+
+void pprocess_signal_tdesc_free(struct posix_thread *thread)
+{
+ struct posix_process *pproc;
+ struct uk_signal *sig;
+ int signum;
+
+ UK_ASSERT(thread);
+ UK_ASSERT(thread->signal);
+
+ pproc = tid2pprocess(thread->tid);
+ UK_ASSERT(pproc);
+
+ pprocess_signal_foreach(signum)
+ while ((sig = pprocess_signal_dequeue(pproc, thread, signum)))
+ uk_signal_free(thread->_a, sig);
+
+ uk_signal_free(thread->process->_a, thread->signal);
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ */
+
+#ifndef __UK_PROCESS_SIGNAL_H__
+#define __UK_PROCESS_SIGNAL_H__
+
+#include <limits.h>
+#include <signal.h>
+
+#include <uk/alloc.h>
+#include <uk/list.h>
+#include <uk/process.h>
+#include <uk/refcount.h>
+#include <uk/semaphore.h>
+
+#include "process.h"
+#include "sigset.h"
+
+#define SIG_ARRAY_COUNT _NSIG /* SIGRTMAX + 1 */
+
+/* Check if signal number is valid */
+#define IS_VALID(_signum) \
+ ((_signum) > 0 && (_signum) <= SIGRTMAX)
+
+/* Check whether signal number is pending in sigqueue */
+#define IS_PENDING(_sigqueue, _signum) \
+ uk_sigismember(&(_sigqueue).pending, (_signum))
+
+/* Set signal number in sigqueue */
+#define SET_PENDING(_sigqueue, _signum) \
+ uk_sigaddset(&(_sigqueue).pending, (_signum))
+
+/* Clear a signal from a sigqueue */
+#define RESET_PENDING(_sigqueue, _signum) \
+ uk_sigdelset(&(_sigqueue).pending, (_signum))
+
+/* Check if posix thread masks signal number */
+#define IS_MASKED(_t, _signum) \
+ uk_sigismember(&(_t)->signal->mask, (_signum))
+
+/* Sets a posix thread's mask of the given signal number */
+#define SET_MASKED(_t, _signum) \
+ uk_sigaddset(&(_t)->signal->mask, (_signum))
+
+/* Check if signal is ignored by a process. A signal is ignored if it
+ * is explicitly set to IGNORE by sigaction, or if it is set to its
+ * default action and that action is IGNORE.
+ */
+#define IS_IGNORED(_pproc, _signum) \
+ ((KERN_SIGACTION(_pproc, _signum)->ks_handler == SIG_IGN) || \
+ (KERN_SIGACTION(_pproc, _signum)->ks_handler == SIG_DFL && \
+ UK_BIT(_signum) & SIGACT_IGN_MASK))
+
+/* Allocate and return `struct uk_signal` or NULL */
+#define uk_signal_alloc(_alloc) \
+ uk_zalloc(_alloc, sizeof(struct uk_signal))
+
+/* Free a previously allocated `struct uk_signal` */
+#define uk_signal_free(_alloc, _sig) \
+ uk_free(_alloc, _sig)
+
+/* Helper to get pointer to kern_sigaction from process,
+ * for a given signal
+ */
+#define KERN_SIGACTION(_proc, _signum) \
+ ({ \
+ UK_ASSERT((_proc)->signal); \
+ UK_ASSERT((_proc)->signal->sigaction); \
+ &(_proc)->signal->sigaction->ks[_signum]; \
+ })
+
+/* Helper to iterate over all signals (std + rt) */
+#define pprocess_signal_foreach(_sn) \
+ for ((_sn) = 1; (__sz)(_sn) < SIG_ARRAY_COUNT; (_sn)++)
+
+/* Helper to iterate over standard signals */
+#define pprocess_stdsig_foreach(_sn) \
+ for ((_sn) = 1; (__sz)(_sn) < SIGRTMIN; (_sn)++)
+
+/* Kernel version of `struct sigaction` passed to rt_sigaction(). The order
+ * of the elements of this struct is different of the `struct sigaction`
+ * passed to the sigaction() glibc wrapper.
+ */
+struct kern_sigaction {
+ void (*ks_handler)(int signo);
+ unsigned long ks_flags;
+ void (*ks_restorer)(void);
+ uk_sigset_t ks_mask;
+};
+
+struct uk_sigaction {
+ struct kern_sigaction ks[SIG_ARRAY_COUNT];
+ struct uk_alloc *alloc;
+ __atomic refcnt;
+};
+
+#if CONFIG_LIBPOSIX_PROCESS_PIDS
+
+/* Descriptor of a pending signal.
+ *
+ * Each pending signal is described as a set of
+ * information `siginfo`. This includes the signal
+ * number, sending pid, address of faulting
+ * instruction, and more. The individual fields of
+ * `siginfo_t` are described in `man siginfo_t`.
+ */
+struct uk_signal {
+ siginfo_t siginfo;
+ struct uk_list_head list_head;
+};
+
+/* Descriptor of a signal queue.
+ *
+ * Pending signals are expressed in a signal set, that
+ * provides information on which signal numbers are pending.
+ *
+ * By POSIX, standard signals are queued once regardless of
+ * the number of times the signal was issued by the sender.
+ * Real-time signals allow in-order delivery of multiple
+ * instances of a given signal.
+ *
+ * This structure maintains an list of `struct uk_signal` for
+ * each signal number. Lists of standard signals can only
+ * contain a single item.
+ */
+struct uk_signal_queue {
+ uk_sigset_t pending;
+ struct uk_list_head list_head[SIG_ARRAY_COUNT];
+};
+
+/* Signal descriptor of a posix_process.
+ *
+ * A process may queue up to `queued_max` signals,
+ * the value of which is set at init time.
+ * POSIX requires at least _POSIX_SIGQUEUE_MAX
+ * signals, while Linux allows the user to configure
+ * that value via rlimit.
+ *
+ * Signals can be targeting the process, or individual
+ * threads. Signals pending for the process are queued
+ * in `sigqueue`. Signals pending for individual
+ * threads are queued in thread-specific queues (see
+ * `struct uk_signal_tdesc`).
+ *
+ * Each signal defines a signal action, which may
+ * be the default one or a custom one specified by
+ * calling `rt_sigaction`. Signal actions are defined
+ * at the process level, i.e. are common for all
+ * threads of a given process.
+ */
+struct uk_signal_pdesc {
+ __sz queued_count;
+ __sz queued_max;
+ struct uk_signal_queue sigqueue;
+ /* We use dynamically allocated memory for sigaction as passing
+ * CLONE_SIGHAND to clone() requires that the parent and the child
+ * share the same set of handlers.
+ */
+ struct uk_sigaction *sigaction;
+};
+
+/* Signal descriptor of a posix_thread.
+ *
+ * Each thread maintains its own signal mask.
+ * That mask applies to signals targeting
+ * that thread, and also impacts the delivery
+ * of process-wide signals, as these are
+ * delivered to the first thread that does
+ * not mask that signal.
+ *
+ * Each thread also maintains its own signal
+ * queue, as besides signals targeting the
+ * process, signals can target individual
+ * threads. For details on signal queues see
+ * the description of `struct uk_signal_queue`.
+ */
+struct uk_signal_tdesc {
+ uk_sigset_t mask;
+ uk_sigset_t sigwait_set;
+ struct uk_signal_queue sigqueue;
+ struct uk_semaphore deliver_semaphore;
+ struct uk_semaphore pending_semaphore;
+};
+
+/* Allocate signal descriptor of a posix process */
+int pprocess_signal_pdesc_alloc(struct posix_process *proc);
+
+/* Allocate signal descriptor of a posix thread */
+int pprocess_signal_tdesc_alloc(struct posix_thread *thread);
+
+/* Initialize signal descriptors of a posix process */
+int pprocess_signal_pdesc_init(struct posix_process *proc);
+
+/* Initialize signal descriptors of a posix thread */
+int pprocess_signal_tdesc_init(struct posix_thread *thread);
+
+/* Free signal descritors of a posix process
+ *
+ * This function is safe to call at thread termination as
+ * we reject signals once the process exits.
+ */
+void pprocess_signal_pdesc_free(struct posix_process *process);
+
+/* Free signal descritors of a posix thread
+ *
+ * This function is safe to call at thread termination as
+ * we reject signals once the process exits.
+ */
+void pprocess_signal_tdesc_free(struct posix_thread *thread);
+
+/* Dequeue a signal from a thread or process
+ *
+ * Setting pthread to NULL signifies that the signal
+ * is queued at the process
+ */
+struct uk_signal *pprocess_signal_dequeue(struct posix_process *pproc,
+ struct posix_thread *pthread,
+ int signum);
+
+/* Allocates and, upon success, acquires a new instance of uk_sigaction.
+ * Does not clear or otherwise initialize signal dispositions.
+ */
+int pprocess_signal_sigaction_new(struct uk_alloc *alloc,
+ struct uk_signal_pdesc *pd);
+
+static inline void pprocess_signal_sigaction_acquire(struct uk_sigaction *sa)
+{
+ uk_refcount_acquire(&sa->refcnt);
+}
+
+static inline void pprocess_signal_sigaction_release(struct uk_sigaction *sa)
+{
+ if (uk_refcount_release(&sa->refcnt))
+ uk_free(sa->alloc, sa);
+}
+
+/* Clears the fields of an instance of struct uk_sigaction */
+void pprocess_signal_sigaction_clear(struct kern_sigaction *ks);
+
+/* Clear pending signal(s) from process. If the signum corresponds to
+ * an std-signal, it clears that an signal from pending. Otherwise,
+ * if signum corresponding an rt-signal it clears all instances from
+ * the queue.
+ */
+void pprocess_signal_clear_pending(struct posix_process *proc, int signum);
+
+/* Get next pending signal of a given thread */
+struct uk_signal *pprocess_signal_next_pending_t(struct posix_thread *pthread);
+
+#endif /* CONFIG_LIBPOSIX_PROCESS_PIDS */
+
+#endif /* __UK_PROCESS_SIGNAL_H__ */