Introduce interruptor tool
An interruptor instance will help to wake up a blocking call from another thread (typically to terminate immediately on Ctrl+C).
This commit is contained in:
parent
0426708544
commit
e0896142db
3 changed files with 162 additions and 0 deletions
|
@ -25,6 +25,7 @@ src = [
|
|||
'src/stream.c',
|
||||
'src/video_buffer.c',
|
||||
'src/util/file.c',
|
||||
'src/util/intr.c',
|
||||
'src/util/log.c',
|
||||
'src/util/net.c',
|
||||
'src/util/process.c',
|
||||
|
|
83
app/src/util/intr.c
Normal file
83
app/src/util/intr.c
Normal file
|
@ -0,0 +1,83 @@
|
|||
#include "intr.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
bool
|
||||
sc_intr_init(struct sc_intr *intr) {
|
||||
bool ok = sc_mutex_init(&intr->mutex);
|
||||
if (!ok) {
|
||||
LOGE("Could not init intr mutex");
|
||||
return false;
|
||||
}
|
||||
|
||||
intr->socket = SC_INVALID_SOCKET;
|
||||
intr->process = SC_PROCESS_NONE;
|
||||
|
||||
atomic_store_explicit(&intr->interrupted, false, memory_order_relaxed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_intr_set_socket(struct sc_intr *intr, sc_socket socket) {
|
||||
assert(intr->process == SC_PROCESS_NONE);
|
||||
|
||||
sc_mutex_lock(&intr->mutex);
|
||||
bool interrupted =
|
||||
atomic_load_explicit(&intr->interrupted, memory_order_relaxed);
|
||||
if (!interrupted) {
|
||||
intr->socket = socket;
|
||||
}
|
||||
sc_mutex_unlock(&intr->mutex);
|
||||
|
||||
return !interrupted;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_intr_set_process(struct sc_intr *intr, sc_pid pid) {
|
||||
assert(intr->socket == SC_INVALID_SOCKET);
|
||||
|
||||
sc_mutex_lock(&intr->mutex);
|
||||
bool interrupted =
|
||||
atomic_load_explicit(&intr->interrupted, memory_order_relaxed);
|
||||
if (!interrupted) {
|
||||
intr->process = pid;
|
||||
}
|
||||
sc_mutex_unlock(&intr->mutex);
|
||||
|
||||
return !interrupted;
|
||||
}
|
||||
|
||||
void
|
||||
sc_intr_interrupt(struct sc_intr *intr) {
|
||||
sc_mutex_lock(&intr->mutex);
|
||||
|
||||
atomic_store_explicit(&intr->interrupted, true, memory_order_relaxed);
|
||||
|
||||
// No more than one component to interrupt
|
||||
assert(intr->socket == SC_INVALID_SOCKET ||
|
||||
intr->process == SC_PROCESS_NONE);
|
||||
|
||||
if (intr->socket != SC_INVALID_SOCKET) {
|
||||
LOGD("Interrupting socket");
|
||||
net_interrupt(intr->socket);
|
||||
intr->socket = SC_INVALID_SOCKET;
|
||||
}
|
||||
if (intr->process != SC_PROCESS_NONE) {
|
||||
LOGD("Interrupting process");
|
||||
sc_process_terminate(intr->process);
|
||||
intr->process = SC_PROCESS_NONE;
|
||||
}
|
||||
|
||||
sc_mutex_unlock(&intr->mutex);
|
||||
}
|
||||
|
||||
void
|
||||
sc_intr_destroy(struct sc_intr *intr) {
|
||||
assert(intr->socket == SC_INVALID_SOCKET);
|
||||
assert(intr->process == SC_PROCESS_NONE);
|
||||
|
||||
sc_mutex_destroy(&intr->mutex);
|
||||
}
|
78
app/src/util/intr.h
Normal file
78
app/src/util/intr.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
#ifndef SC_INTR_H
|
||||
#define SC_INTR_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "net.h"
|
||||
#include "process.h"
|
||||
#include "thread.h"
|
||||
|
||||
/**
|
||||
* Interruptor to wake up a blocking call from another thread
|
||||
*
|
||||
* It allows to register a socket or a process before a blocking call, and
|
||||
* interrupt/close from another thread to wake up the blocking call.
|
||||
*/
|
||||
struct sc_intr {
|
||||
sc_mutex mutex;
|
||||
|
||||
sc_socket socket;
|
||||
sc_pid process;
|
||||
|
||||
// Written protected by the mutex to avoid race conditions against
|
||||
// sc_intr_set_socket() and sc_intr_set_process(), but can be read
|
||||
// (atomically) without mutex
|
||||
atomic_bool interrupted;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize an interruptor
|
||||
*/
|
||||
bool
|
||||
sc_intr_init(struct sc_intr *intr);
|
||||
|
||||
/**
|
||||
* Set a socket as the interruptible component
|
||||
*
|
||||
* Call with SC_INVALID_SOCKET to unset.
|
||||
*/
|
||||
bool
|
||||
sc_intr_set_socket(struct sc_intr *intr, sc_socket socket);
|
||||
|
||||
/**
|
||||
* Set a process as the interruptible component
|
||||
*
|
||||
* Call with SC_PROCESS_NONE to unset.
|
||||
*/
|
||||
bool
|
||||
sc_intr_set_process(struct sc_intr *intr, sc_pid socket);
|
||||
|
||||
/**
|
||||
* Interrupt the current interruptible component
|
||||
*
|
||||
* Must be called from a different thread.
|
||||
*/
|
||||
void
|
||||
sc_intr_interrupt(struct sc_intr *intr);
|
||||
|
||||
/**
|
||||
* Read the interrupted state
|
||||
*
|
||||
* It is exposed as a static inline function because it just loads from an
|
||||
* atomic.
|
||||
*/
|
||||
static inline bool
|
||||
sc_intr_is_interrupted(struct sc_intr *intr) {
|
||||
return atomic_load_explicit(&intr->interrupted, memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the interruptor
|
||||
*/
|
||||
void
|
||||
sc_intr_destroy(struct sc_intr *intr);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue