IPC: fix resource cleanup if the server dies

Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
This commit is contained in:
Angus Salkeld 2012-01-12 15:09:58 +11:00
parent b32a027d5b
commit 477fac4e01
6 changed files with 104 additions and 13 deletions

View File

@ -112,6 +112,7 @@ struct qb_ipcc_connection {
struct qb_ipcc_funcs funcs;
struct qb_ipc_request_header *receive_buf;
uint32_t fc_enable_max;
int32_t is_connected;
};
int32_t qb_ipcc_us_setup_connect(struct qb_ipcc_connection *c,

View File

@ -22,6 +22,7 @@
#include "ipc_int.h"
#include "util_int.h"
#include "ringbuffer_int.h"
#include <qb/qbdefs.h>
#include <qb/qbatomic.h>
#include <qb/qbloop.h>
@ -38,9 +39,15 @@
static void
qb_ipcc_shm_disconnect(struct qb_ipcc_connection *c)
{
qb_rb_close(c->request.u.shm.rb);
qb_rb_close(c->response.u.shm.rb);
qb_rb_close(c->event.u.shm.rb);
if (c->is_connected) {
qb_rb_close(c->request.u.shm.rb);
qb_rb_close(c->response.u.shm.rb);
qb_rb_close(c->event.u.shm.rb);
} else {
qb_rb_force_close(c->request.u.shm.rb);
qb_rb_force_close(c->response.u.shm.rb);
qb_rb_force_close(c->event.u.shm.rb);
}
}
static ssize_t

View File

@ -85,6 +85,7 @@ qb_ipcc_connect(const char *name, size_t max_msg_size)
if (res != 0) {
goto disconnect_and_cleanup;
}
c->is_connected = QB_TRUE;
return c;
disconnect_and_cleanup:
@ -123,6 +124,7 @@ qb_ipcc_send(struct qb_ipcc_connection * c, const void *msg_ptr, size_t msg_len)
res2 = qb_ipc_us_send(&c->setup, msg_ptr, 1);
} while (res2 == -EAGAIN);
if (res2 == -EPIPE) {
c->is_connected = QB_FALSE;
return -ENOTCONN;
}
if (res2 != 1) {
@ -177,6 +179,7 @@ qb_ipcc_sendv(struct qb_ipcc_connection * c, const struct iovec * iov,
res2 = qb_ipc_us_send(&c->setup, &res, 1);
} while (res2 == -EAGAIN);
if (res2 == -EPIPE) {
c->is_connected = QB_FALSE;
return -ENOTCONN;
}
if (res2 != 1) {
@ -200,6 +203,9 @@ qb_ipcc_recv(struct qb_ipcc_connection * c, void *msg_ptr,
if ((res == -EAGAIN || res == -ETIMEDOUT) && c->needs_sock_for_poll) {
res2 = qb_ipc_us_recv_ready(&c->setup, 0);
if (res2 < 0) {
if (res2 == -ENOTCONN) {
c->is_connected = QB_FALSE;
}
return res2;
} else {
return res;
@ -275,6 +281,9 @@ qb_ipcc_event_recv(struct qb_ipcc_connection * c, void *msg_pt,
if (ow) {
res = qb_ipc_us_recv_ready(ow, ms_timeout);
if (res < 0) {
if (res == -ENOTCONN) {
c->is_connected = QB_FALSE;
}
return res;
}
}
@ -285,6 +294,9 @@ qb_ipcc_event_recv(struct qb_ipcc_connection * c, void *msg_pt,
if (c->needs_sock_for_poll) {
res = qb_ipc_us_recv(&c->setup, &one_byte, 1, -1);
if (res < 0) {
if (res == -ENOTCONN) {
c->is_connected = QB_FALSE;
}
return res;
}
}

View File

@ -247,6 +247,24 @@ qb_rb_close(qb_ringbuffer_t * rb)
free(rb);
}
void
qb_rb_force_close(qb_ringbuffer_t * rb)
{
if (rb == NULL) {
return;
}
(void)rb->sem_destroy_fn(rb);
unlink(rb->shared_hdr->data_path);
unlink(rb->shared_hdr->hdr_path);
qb_util_log(LOG_DEBUG,
"Force free'ing ringbuffer: %s",
rb->shared_hdr->hdr_path);
munmap(rb->shared_data, (rb->shared_hdr->size * sizeof(uint32_t)) << 1);
munmap(rb->shared_hdr, sizeof(struct qb_ringbuffer_shared_s));
free(rb);
}
char *
qb_rb_name_get(qb_ringbuffer_t * rb)
{

View File

@ -71,6 +71,9 @@ struct qb_ringbuffer_s {
qb_rb_sem_destroy_fn_t sem_destroy_fn;
};
void qb_rb_force_close(qb_ringbuffer_t * rb);
#if defined(_SEM_SEMUN_UNDEFINED)
union semun {
int32_t val;

View File

@ -262,10 +262,12 @@ repeat_send:
qb_perror(LOG_DEBUG, "qb_ipcc_recv");
return res;
}
ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header));
ck_assert_int_eq(res_header.id, IPC_MSG_RES_TX_RX);
ck_assert_int_eq(res_header.size, sizeof(struct qb_ipc_response_header));
return 0;
if (expect_perfection) {
ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header));
ck_assert_int_eq(res_header.id, IPC_MSG_RES_TX_RX);
ck_assert_int_eq(res_header.size, sizeof(struct qb_ipc_response_header));
}
return res;
}
static int32_t recv_timeout = -1;
@ -297,7 +299,7 @@ test_ipc_txrx(void)
size *= 2;
if (size >= MAX_MSG_SIZE)
break;
if (send_and_check(size, recv_timeout) < 0) {
if (send_and_check(size, recv_timeout, QB_TRUE) < 0) {
break;
}
}
@ -308,6 +310,54 @@ test_ipc_txrx(void)
stop_process(pid);
}
static void
test_ipc_exit(void)
{
int32_t c = 0;
int32_t j = 0;
int32_t rc = 0;
pid_t pid;
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, MAX_MSG_SIZE);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
/* confirm we can send one message */
rc = send_and_check(100, recv_timeout, QB_TRUE);
_ck_assert_int(rc, >=, 0);
/* kill the server */
stop_process(pid);
/* assertion: we can call send/recv without locking up */
rc = send_and_check(100, recv_timeout, QB_FALSE);
ck_assert_int_eq(rc, -ENOTCONN);
/* this needs to free up the shared mem */
qb_ipcc_disconnect(conn);
}
START_TEST(test_ipc_exit_shm)
{
ipc_type = QB_IPC_SHM;
ipc_name = __func__;
recv_timeout = 1000;
test_ipc_exit();
}
END_TEST
START_TEST(test_ipc_txrx_shm_tmo)
{
turn_on_fc = QB_FALSE;
@ -469,8 +519,6 @@ END_TEST
static void
test_ipc_server_fail(void)
#ifdef IPC_HANDLES_FAILED_SERVER
{
struct qb_ipc_request_header req_header;
struct qb_ipc_response_header res_header;
@ -544,7 +592,6 @@ START_TEST(test_ipc_server_fail_shm)
test_ipc_server_fail();
}
END_TEST
#endif /* IPC_HANDLES_FAILED_SERVER */
static Suite *
ipc_suite(void)
@ -553,7 +600,6 @@ ipc_suite(void)
uid_t uid;
Suite *s = suite_create("ipc");
#ifdef IPC_HANDLES_FAILED_SERVER
tc = tcase_create("ipc_server_fail_shm");
tcase_add_test(tc, test_ipc_server_fail_shm);
tcase_set_timeout(tc, 6);
@ -563,7 +609,6 @@ ipc_suite(void)
tcase_add_test(tc, test_ipc_server_fail_soc);
tcase_set_timeout(tc, 6);
suite_add_tcase(s, tc);
#endif /* IPC_HANDLES_FAILED_SERVER */
tc = tcase_create("ipc_txrx_shm_block");
tcase_add_test(tc, test_ipc_txrx_shm_block);
@ -617,6 +662,11 @@ ipc_suite(void)
tcase_set_timeout(tc, 16);
suite_add_tcase(s, tc);
tc = tcase_create("ipc_exit_shm");
tcase_add_test(tc, test_ipc_exit_shm);
tcase_set_timeout(tc, 3);
suite_add_tcase(s, tc);
return s;
}