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 "Backtrace.h"
|
||||
|
||||
static bool Running = false;
|
||||
static bool Abort = false;
|
||||
static bool Stop = false;
|
||||
static volatile sig_atomic_t Running = 0;
|
||||
static volatile sig_atomic_t AbortSignal = 0;
|
||||
static volatile sig_atomic_t StopSignal = 0;
|
||||
|
||||
static void abortHandler(int sig)
|
||||
{
|
||||
if (!Abort) {
|
||||
traceInfo(sig);
|
||||
}
|
||||
Abort = true;
|
||||
if (!Running) {
|
||||
abort();
|
||||
// Signal handlers must be async-signal-safe: only set flags here.
|
||||
if (!AbortSignal) {
|
||||
AbortSignal = sig;
|
||||
}
|
||||
}
|
||||
|
||||
static void stopHandler(int sig)
|
||||
{
|
||||
Stop = true;
|
||||
if (!Running) {
|
||||
abort();
|
||||
// Signal handlers must be async-signal-safe: only set flags here.
|
||||
if (!StopSignal) {
|
||||
StopSignal = sig;
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,18 +138,21 @@ int Proxy::run()
|
||||
logNotice("predixy running with Name:%s Workers:%d",
|
||||
mConf->name(),
|
||||
(int)mHandlers.size());
|
||||
Running = 1;
|
||||
std::vector<std::shared_ptr<std::thread>> tasks;
|
||||
for (auto h : mHandlers) {
|
||||
std::shared_ptr<std::thread> t(new std::thread([=](){h->run();}));
|
||||
tasks.push_back(t);
|
||||
}
|
||||
Running = true;
|
||||
bool stop = false;
|
||||
while (!stop) {
|
||||
if (Abort) {
|
||||
if (AbortSignal) {
|
||||
int sig = AbortSignal;
|
||||
AbortSignal = 0;
|
||||
traceInfo(sig);
|
||||
stop = true;
|
||||
abort();
|
||||
} else if (Stop) {
|
||||
} else if (StopSignal) {
|
||||
fprintf(stderr, "predixy will quit ASAP Bye!\n");
|
||||
stop = true;
|
||||
}
|
||||
|
||||
@ -144,6 +144,7 @@ TESTS=(
|
||||
"test/request_parser_error_log.py"
|
||||
"test/response_parser_error_log.py"
|
||||
"test/request_parser_long_command.py"
|
||||
"test/signal_handling.py"
|
||||
"test/pubsub_long_name.py"
|
||||
"test/pubsub_large_message.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