diff --git a/lib/ipc_int.h b/lib/ipc_int.h index 72af15f..7f37038 100644 --- a/lib/ipc_int.h +++ b/lib/ipc_int.h @@ -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, diff --git a/lib/ipc_shm.c b/lib/ipc_shm.c index 1f38f26..d390939 100644 --- a/lib/ipc_shm.c +++ b/lib/ipc_shm.c @@ -22,6 +22,7 @@ #include "ipc_int.h" #include "util_int.h" +#include "ringbuffer_int.h" #include #include #include @@ -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 diff --git a/lib/ipcc.c b/lib/ipcc.c index ca1309a..5f2d439 100644 --- a/lib/ipcc.c +++ b/lib/ipcc.c @@ -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; } } diff --git a/lib/ringbuffer.c b/lib/ringbuffer.c index c983bf6..4259bf0 100644 --- a/lib/ringbuffer.c +++ b/lib/ringbuffer.c @@ -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) { diff --git a/lib/ringbuffer_int.h b/lib/ringbuffer_int.h index 6866fd6..d2f24fa 100644 --- a/lib/ringbuffer_int.h +++ b/lib/ringbuffer_int.h @@ -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; diff --git a/tests/check_ipc.c b/tests/check_ipc.c index aa6683e..95919b4 100644 --- a/tests/check_ipc.c +++ b/tests/check_ipc.c @@ -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; }