mirror of
https://github.com/joyieldInc/predixy.git
synced 2026-02-05 01:42:24 +08:00
Make signal handlers async-signal-safe
This commit is contained in:
parent
d4b10d5065
commit
33601ea2f0
@ -20,26 +20,23 @@
|
|||||||
#include "RequestParser.h"
|
#include "RequestParser.h"
|
||||||
#include "Backtrace.h"
|
#include "Backtrace.h"
|
||||||
|
|
||||||
static bool Running = false;
|
static volatile sig_atomic_t Running = 0;
|
||||||
static bool Abort = false;
|
static volatile sig_atomic_t AbortSignal = 0;
|
||||||
static bool Stop = false;
|
static volatile sig_atomic_t StopSignal = 0;
|
||||||
|
|
||||||
static void abortHandler(int sig)
|
static void abortHandler(int sig)
|
||||||
{
|
{
|
||||||
if (!Abort) {
|
// Signal handlers must be async-signal-safe: only set flags here.
|
||||||
traceInfo(sig);
|
if (!AbortSignal) {
|
||||||
}
|
AbortSignal = sig;
|
||||||
Abort = true;
|
|
||||||
if (!Running) {
|
|
||||||
abort();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stopHandler(int sig)
|
static void stopHandler(int sig)
|
||||||
{
|
{
|
||||||
Stop = true;
|
// Signal handlers must be async-signal-safe: only set flags here.
|
||||||
if (!Running) {
|
if (!StopSignal) {
|
||||||
abort();
|
StopSignal = sig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,18 +138,21 @@ int Proxy::run()
|
|||||||
logNotice("predixy running with Name:%s Workers:%d",
|
logNotice("predixy running with Name:%s Workers:%d",
|
||||||
mConf->name(),
|
mConf->name(),
|
||||||
(int)mHandlers.size());
|
(int)mHandlers.size());
|
||||||
|
Running = 1;
|
||||||
std::vector<std::shared_ptr<std::thread>> tasks;
|
std::vector<std::shared_ptr<std::thread>> tasks;
|
||||||
for (auto h : mHandlers) {
|
for (auto h : mHandlers) {
|
||||||
std::shared_ptr<std::thread> t(new std::thread([=](){h->run();}));
|
std::shared_ptr<std::thread> t(new std::thread([=](){h->run();}));
|
||||||
tasks.push_back(t);
|
tasks.push_back(t);
|
||||||
}
|
}
|
||||||
Running = true;
|
|
||||||
bool stop = false;
|
bool stop = false;
|
||||||
while (!stop) {
|
while (!stop) {
|
||||||
if (Abort) {
|
if (AbortSignal) {
|
||||||
|
int sig = AbortSignal;
|
||||||
|
AbortSignal = 0;
|
||||||
|
traceInfo(sig);
|
||||||
stop = true;
|
stop = true;
|
||||||
abort();
|
abort();
|
||||||
} else if (Stop) {
|
} else if (StopSignal) {
|
||||||
fprintf(stderr, "predixy will quit ASAP Bye!\n");
|
fprintf(stderr, "predixy will quit ASAP Bye!\n");
|
||||||
stop = true;
|
stop = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,6 +144,7 @@ TESTS=(
|
|||||||
"test/request_parser_error_log.py"
|
"test/request_parser_error_log.py"
|
||||||
"test/response_parser_error_log.py"
|
"test/response_parser_error_log.py"
|
||||||
"test/request_parser_long_command.py"
|
"test/request_parser_long_command.py"
|
||||||
|
"test/signal_handling.py"
|
||||||
"test/pubsub_long_name.py"
|
"test/pubsub_long_name.py"
|
||||||
"test/pubsub_large_message.py"
|
"test/pubsub_large_message.py"
|
||||||
"test/transaction_forbid.py"
|
"test/transaction_forbid.py"
|
||||||
|
|||||||
90
test/signal_handling.py
Normal file
90
test/signal_handling.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Start a temporary predixy instance and verify SIGTERM shuts it down cleanly.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
from test_util import parse_args, exit_with_result
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_port(host, port, timeout=5.0):
|
||||||
|
deadline = time.time() + timeout
|
||||||
|
while time.time() < deadline:
|
||||||
|
try:
|
||||||
|
with socket.create_connection((host, port), timeout=0.5):
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
time.sleep(0.05)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def start_predixy(root, redis_port):
|
||||||
|
predixy_bin = os.path.join(root, "src", "predixy")
|
||||||
|
if not os.path.exists(predixy_bin):
|
||||||
|
raise RuntimeError("predixy binary not found")
|
||||||
|
|
||||||
|
listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
listen_sock.bind(("127.0.0.1", 0))
|
||||||
|
listen_port = listen_sock.getsockname()[1]
|
||||||
|
listen_sock.close()
|
||||||
|
|
||||||
|
tmp_dir = tempfile.TemporaryDirectory()
|
||||||
|
conf_path = os.path.join(tmp_dir.name, "predixy_test.conf")
|
||||||
|
with open(conf_path, "w") as f:
|
||||||
|
f.write(
|
||||||
|
"Name PredixySignalTest\n"
|
||||||
|
f"Bind 127.0.0.1:{listen_port}\n"
|
||||||
|
"WorkerThreads 1\n"
|
||||||
|
"ClientTimeout 3\n"
|
||||||
|
"LogVerbSample 0\n"
|
||||||
|
"LogDebugSample 0\n"
|
||||||
|
"LogInfoSample 10000\n"
|
||||||
|
"LogNoticeSample 1\n"
|
||||||
|
"LogWarnSample 1\n"
|
||||||
|
"LogErrorSample 1\n"
|
||||||
|
"\n"
|
||||||
|
"StandaloneServerPool {\n"
|
||||||
|
" RefreshMethod fixed\n"
|
||||||
|
" Group test {\n"
|
||||||
|
f" + 127.0.0.1:{redis_port}\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
proc = subprocess.Popen([predixy_bin, conf_path],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL)
|
||||||
|
if not wait_for_port("127.0.0.1", listen_port, timeout=5.0):
|
||||||
|
proc.terminate()
|
||||||
|
tmp_dir.cleanup()
|
||||||
|
raise RuntimeError("predixy did not start")
|
||||||
|
|
||||||
|
return proc, tmp_dir
|
||||||
|
|
||||||
|
|
||||||
|
def run_test(project_root, redis_port):
|
||||||
|
proc, tmp_dir = start_predixy(project_root, redis_port)
|
||||||
|
try:
|
||||||
|
proc.send_signal(signal.SIGTERM)
|
||||||
|
try:
|
||||||
|
proc.wait(timeout=5.0)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
print("FAIL: predixy did not exit after SIGTERM")
|
||||||
|
proc.kill()
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
tmp_dir.cleanup()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = parse_args("Signal handling test", default_port=6380)
|
||||||
|
root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
success = run_test(root, args.port)
|
||||||
|
exit_with_result(success, "signal handling", "signal handling")
|
||||||
Loading…
Reference in New Issue
Block a user