Просмотр исходного кода

qnetd: Add support for qnetd algo timer

Algo timer is simplified timer designed for qnetd algorithm. Instead of
full timer only one can exists per client. Workflow is:
- In one of algorithm callbacks qnetd_client_algo_timer_schedule is
  called
- On timeout .timer_callback is called (for example
  qnetd_algo_test_timer_callback). It's possible to set send_vote and
  result_vote to send vote info to client
- It's possible to discard timer by calling
  qnetd_client_algo_timer_abort

Timer is automatically deleted on client disconnect.

To make all this possible, qnetd main loop now has support for
timer-list (main_timer_list). To be able to handle error and disconnect
client from timer callback, client has schedule_disconnect. If this is
set to 1, client is disconnected on current call of poll loop.

Signed-off-by: Jan Friesse <jfriesse@redhat.com>
Jan Friesse 10 лет назад
Родитель
Сommit
04ac73018e

+ 2 - 1
qdevices/Makefile.am

@@ -45,7 +45,8 @@ corosync_qnetd_SOURCES	= corosync-qnetd.c dynar.c msg.c msgio.c nss-sock.c  \
 			    node-list.c qnetd-algo-test.c qnetd-algorithm.c qnetd-algo-utils.c \
 			    qnetd-algo-ffsplit.c qnetd-cluster-list.c qnetd-client-send.c \
 			    qnetd-algo-2nodelms.c qnetd-algo-lms.c utils.c qnetd-instance.c \
-			    qnetd-client-net.c qnetd-client-msg-received.c qnetd-log-debug.c
+			    qnetd-client-net.c qnetd-client-msg-received.c qnetd-log-debug.c \
+			    qnetd-client-algo-timer.c
 
 corosync_qdevice_SOURCES	= corosync-qdevice.c qdevice-cmap.c qdevice-instance.c node-list.c \
 				  utils.c qdevice-log.c qdevice-log-debug.c qdevice-votequorum.c \

+ 12 - 6
qdevices/corosync-qnetd.c

@@ -96,7 +96,9 @@ qnetd_poll(struct qnetd_instance *instance)
 	}
 
 	if ((poll_res = PR_Poll(pfds, qnetd_poll_array_size(&instance->poll_array),
-	    PR_INTERVAL_NO_TIMEOUT)) > 0) {
+	    timer_list_time_to_expire(&instance->main_timer_list))) >= 0) {
+		timer_list_expire(&instance->main_timer_list);
+
 		/*
 		 * Walk thru pfds array and process events
 		 */
@@ -112,11 +114,13 @@ qnetd_poll(struct qnetd_instance *instance)
 					client = client_next;
 					client_next = TAILQ_NEXT(client, entries);
 				}
+				client_disconnect = client->schedule_disconnect;
+			} else {
+				client_disconnect = 0;
 			}
 
-			client_disconnect = 0;
-
-			if (!client_disconnect && pfds[i].out_flags & PR_POLL_READ) {
+			if (!client_disconnect && poll_res > 0 &&
+			    pfds[i].out_flags & PR_POLL_READ) {
 				if (i == 0) {
 					qnetd_client_net_accept(instance);
 				} else {
@@ -126,7 +130,8 @@ qnetd_poll(struct qnetd_instance *instance)
 				}
 			}
 
-			if (!client_disconnect && pfds[i].out_flags & PR_POLL_WRITE) {
+			if (!client_disconnect && poll_res > 0 &&
+			    pfds[i].out_flags & PR_POLL_WRITE) {
 				if (i == 0) {
 					/*
 					 * Poll write on listen socket -> fatal error
@@ -141,7 +146,7 @@ qnetd_poll(struct qnetd_instance *instance)
 				}
 			}
 
-			if (!client_disconnect &&
+			if (!client_disconnect && poll_res > 0 &&
 			    (pfds[i].out_flags & (PR_POLL_ERR|PR_POLL_NVAL|PR_POLL_HUP|PR_POLL_EXCEPT)) &&
 			    !(pfds[i].out_flags & (PR_POLL_READ|PR_POLL_WRITE))) {
 				if (i == 0) {
@@ -175,6 +180,7 @@ qnetd_poll(struct qnetd_instance *instance)
 		}
 	}
 
+
 	return (0);
 }
 

+ 3 - 1
qdevices/qdevice-model-net.c

@@ -105,7 +105,9 @@ qdevice_model_net_destroy(struct qdevice_instance *instance)
 		qdevice_log_nss(LOG_WARNING, "Can't shutdown NSS");
 	}
 
-	PR_Cleanup();
+	if (PR_Cleanup() != SECSuccess) {
+		qdevice_log_nss(LOG_WARNING, "Can't shutdown NSPR");
+	}
 
 	free(net_instance);
 

+ 6 - 1
qdevices/qdevice-net-cast-vote-timer.c

@@ -115,8 +115,13 @@ qdevice_net_cast_vote_timer_update(struct qdevice_net_instance *instance, enum t
 				return (-1);
 			} else {
 				qdevice_log(LOG_DEBUG, "Cast vote timer is now scheduled every "
-				    "%"PRIu32"ms.", instance->cast_vote_timer_interval);
+				    "%"PRIu32"ms voting %s.", instance->cast_vote_timer_interval,
+				    tlv_vote_to_str(instance->cast_vote_timer_vote));
 			}
+		} else {
+			qdevice_log(LOG_DEBUG, "Cast vote timer remains scheduled every "
+			    "%"PRIu32"ms voting %s.", instance->cast_vote_timer_interval,
+			    tlv_vote_to_str(instance->cast_vote_timer_vote));
 		}
 
 		if (qdevice_net_cast_vote_timer_callback((void *)instance, NULL) != -1) {

+ 8 - 0
qdevices/qnetd-algo-2nodelms.c

@@ -295,6 +295,13 @@ qnetd_algo_2nodelms_vote_info_reply_received(struct qnetd_client *client, uint32
 	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
 }
 
+enum tlv_reply_error_code
+qnetd_algo_2nodelms_timer_callback(struct qnetd_client *client, int *reschedule_timer,
+    int *send_vote, enum tlv_vote *result_vote)
+{
+
+	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
 
 static struct qnetd_algorithm qnetd_algo_2nodelms = {
 	.init                          = qnetd_algo_2nodelms_client_init,
@@ -304,6 +311,7 @@ static struct qnetd_algorithm qnetd_algo_2nodelms = {
 	.client_disconnect             = qnetd_algo_2nodelms_client_disconnect,
 	.ask_for_vote_received         = qnetd_algo_2nodelms_ask_for_vote_received,
 	.vote_info_reply_received      = qnetd_algo_2nodelms_vote_info_reply_received,
+	.timer_callback                = qnetd_algo_2nodelms_timer_callback,
 };
 
 enum tlv_reply_error_code qnetd_algo_2nodelms_register()

+ 4 - 0
qdevices/qnetd-algo-2nodelms.h

@@ -66,6 +66,10 @@ extern enum tlv_reply_error_code	qnetd_algo_2nodelms_ask_for_vote_received(
 extern enum tlv_reply_error_code	qnetd_algo_2nodelms_vote_info_reply_received(
     struct qnetd_client *client, uint32_t msg_seq_num);
 
+extern enum tlv_reply_error_code	qnetd_algo_2nodelms_timer_callback(
+    struct qnetd_client *client, int *reschedule_timer,
+    int *send_vote, enum tlv_vote *result_vote);
+
 extern enum tlv_reply_error_code qnetd_algo_2nodelms_register(void);
 
 

+ 9 - 0
qdevices/qnetd-algo-ffsplit.c

@@ -116,6 +116,14 @@ qnetd_algo_ffsplit_vote_info_reply_received(struct qnetd_client *client, uint32_
 	return (TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM_MESSAGE);
 }
 
+enum tlv_reply_error_code
+qnetd_algo_ffsplit_timer_callback(struct qnetd_client *client, int *reschedule_timer,
+    int *send_vote, enum tlv_vote *result_vote)
+{
+
+	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
 static struct qnetd_algorithm qnetd_algo_ffsplit = {
 	.init                          = qnetd_algo_ffsplit_client_init,
 	.config_node_list_received     = qnetd_algo_ffsplit_config_node_list_received,
@@ -124,6 +132,7 @@ static struct qnetd_algorithm qnetd_algo_ffsplit = {
 	.client_disconnect             = qnetd_algo_ffsplit_client_disconnect,
 	.ask_for_vote_received         = qnetd_algo_ffsplit_ask_for_vote_received,
 	.vote_info_reply_received      = qnetd_algo_ffsplit_vote_info_reply_received,
+	.timer_callback                = qnetd_algo_ffsplit_timer_callback,
 };
 
 enum tlv_reply_error_code qnetd_algo_ffsplit_register()

+ 4 - 0
qdevices/qnetd-algo-ffsplit.h

@@ -66,6 +66,10 @@ extern enum tlv_reply_error_code	qnetd_algo_ffsplit_ask_for_vote_received(
 extern enum tlv_reply_error_code	qnetd_algo_ffsplit_vote_info_reply_received(
     struct qnetd_client *client, uint32_t msg_seq_num);
 
+extern enum tlv_reply_error_code	qnetd_algo_ffsplit_timer_callback(
+    struct qnetd_client *client, int *reschedule_timer,
+    int *send_vote, enum tlv_vote *result_vote);
+
 extern enum tlv_reply_error_code qnetd_algo_ffsplit_register(void);
 
 #ifdef __cplusplus

+ 8 - 0
qdevices/qnetd-algo-lms.c

@@ -338,6 +338,13 @@ qnetd_algo_lms_vote_info_reply_received(struct qnetd_client *client, uint32_t ms
 	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
 }
 
+enum tlv_reply_error_code
+qnetd_algo_lms_timer_callback(struct qnetd_client *client, int *reschedule_timer,
+    int *send_vote, enum tlv_vote *result_vote)
+{
+
+	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
 
 static struct qnetd_algorithm qnetd_algo_lms = {
 	.init                          = qnetd_algo_lms_client_init,
@@ -347,6 +354,7 @@ static struct qnetd_algorithm qnetd_algo_lms = {
 	.client_disconnect             = qnetd_algo_lms_client_disconnect,
 	.ask_for_vote_received         = qnetd_algo_lms_ask_for_vote_received,
 	.vote_info_reply_received      = qnetd_algo_lms_vote_info_reply_received,
+	.timer_callback                = qnetd_algo_lms_timer_callback,
 };
 
 enum tlv_reply_error_code qnetd_algo_lms_register()

+ 4 - 0
qdevices/qnetd-algo-lms.h

@@ -66,6 +66,10 @@ extern enum tlv_reply_error_code	qnetd_algo_lms_ask_for_vote_received(
 extern enum tlv_reply_error_code	qnetd_algo_lms_vote_info_reply_received(
     struct qnetd_client *client, uint32_t msg_seq_num);
 
+extern enum tlv_reply_error_code	qnetd_algo_lms_timer_callback(
+    struct qnetd_client *client, int *reschedule_timer,
+    int *send_vote, enum tlv_vote *result_vote);
+
 extern enum tlv_reply_error_code qnetd_algo_lms_register(void);
 
 

+ 28 - 7
qdevices/qnetd-algo-test.c

@@ -41,6 +41,7 @@
 #include "qnetd-cluster-list.h"
 #include "qnetd-client-send.h"
 #include "qnetd-log-debug.h"
+#include "qnetd-client-algo-timer.h"
 
 /*
  * Called right after client sent init message. This happens after initial accept of client,
@@ -192,15 +193,35 @@ qnetd_algo_test_vote_info_reply_received(struct qnetd_client *client, uint32_t m
 	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
 }
 
+/*
+ * Called as a result of qnetd_client_algo_timer_schedule function call after timeout expires.
+ *
+ * If send_vote is set by callback to non zero value, result_vote must also be set and such vote is
+ * send to client. Result_vote is ignored if send_vote = 0 (default).
+ *
+ * If reschedule timer (default value = 0) is set to non zero value, callback is called again later
+ * with same timeout as originaly created.
+ *
+ * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_*
+ * on failure (error is send back to client)
+ */
+enum tlv_reply_error_code
+qnetd_algo_test_timer_callback(struct qnetd_client *client, int *reschedule_timer,
+    int *send_vote, enum tlv_vote *result_vote)
+{
+
+	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
 
 static struct qnetd_algorithm qnetd_algo_test = {
-	.init                          = qnetd_algo_test_client_init,
-	.config_node_list_received     = qnetd_algo_test_config_node_list_received,
-	.membership_node_list_received = qnetd_algo_test_membership_node_list_received,
-	.quorum_node_list_received     = qnetd_algo_test_quorum_node_list_received,
-	.client_disconnect             = qnetd_algo_test_client_disconnect,
-	.ask_for_vote_received         = qnetd_algo_test_ask_for_vote_received,
-	.vote_info_reply_received      = qnetd_algo_test_vote_info_reply_received,
+	.init				= qnetd_algo_test_client_init,
+	.config_node_list_received	= qnetd_algo_test_config_node_list_received,
+	.membership_node_list_received	= qnetd_algo_test_membership_node_list_received,
+	.quorum_node_list_received	= qnetd_algo_test_quorum_node_list_received,
+	.client_disconnect		= qnetd_algo_test_client_disconnect,
+	.ask_for_vote_received		= qnetd_algo_test_ask_for_vote_received,
+	.vote_info_reply_received	= qnetd_algo_test_vote_info_reply_received,
+	.timer_callback			= qnetd_algo_test_timer_callback,
 };
 
 enum tlv_reply_error_code qnetd_algo_test_register()

+ 4 - 0
qdevices/qnetd-algo-test.h

@@ -66,6 +66,10 @@ extern enum tlv_reply_error_code	qnetd_algo_test_ask_for_vote_received(
 extern enum tlv_reply_error_code	qnetd_algo_test_vote_info_reply_received(
     struct qnetd_client *client, uint32_t msg_seq_num);
 
+extern enum tlv_reply_error_code	qnetd_algo_test_timer_callback(
+    struct qnetd_client *client, int *reschedule_timer,
+    int *send_vote, enum tlv_vote *result_vote);
+
 extern enum tlv_reply_error_code qnetd_algo_test_register(void);
 
 #ifdef __cplusplus

+ 14 - 0
qdevices/qnetd-algorithm.c

@@ -158,6 +158,20 @@ qnetd_algorithm_vote_info_reply_received(struct qnetd_client *client, uint32_t m
 
 }
 
+enum tlv_reply_error_code
+qnetd_algorithm_timer_callback(struct qnetd_client *client, int *reschedule_timer,
+    int *send_vote, enum tlv_vote *result_vote)
+{
+
+	if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qnetd_algorithm_array[client->decision_algorithm] == NULL) {
+		errx(1, "qnetd_algorithm_timer_callback unhandled decision algorithm");
+		return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+	}
+
+	return (qnetd_algorithm_array[client->decision_algorithm]->timer_callback(
+		client, reschedule_timer, send_vote, result_vote));
+}
 
 enum tlv_reply_error_code
 qnetd_algorithm_register(enum tlv_decision_algorithm_type algorithm_number,

+ 5 - 0
qdevices/qnetd-algorithm.h

@@ -69,6 +69,9 @@ extern enum tlv_reply_error_code	qnetd_algorithm_ask_for_vote_received(
 extern enum tlv_reply_error_code	qnetd_algorithm_vote_info_reply_received(
     struct qnetd_client *client, uint32_t msg_seq_num);
 
+extern enum tlv_reply_error_code	qnetd_algorithm_timer_callback(
+    struct qnetd_client *client, int *reschedule_timer, int *send_vote, enum tlv_vote *result_vote);
+
 struct qnetd_algorithm {
 	enum tlv_reply_error_code (*init)(struct qnetd_client *client);
 
@@ -94,6 +97,8 @@ struct qnetd_algorithm {
 	enum tlv_reply_error_code (*vote_info_reply_received)(struct qnetd_client *client,
 	    uint32_t msg_seq_num);
 
+	 enum tlv_reply_error_code (*timer_callback)(struct qnetd_client *client,
+	    int *reschedule_timer, int *send_vote, enum tlv_vote *result_vote);
 };
 
 extern enum tlv_reply_error_code	qnetd_algorithm_register(

+ 145 - 0
qdevices/qnetd-client-algo-timer.c

@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qnetd-log.h"
+#include "qnetd-client-algo-timer.h"
+#include "qnetd-client-send.h"
+#include "qnetd-algorithm.h"
+#include "timer-list.h"
+
+static int
+qnetd_client_algo_timer_callback(void *data1, void *data2)
+{
+	struct qnetd_client *client;
+	enum tlv_vote result_vote;
+	int send_vote;
+	int reschedule_timer;
+	enum tlv_reply_error_code reply_error_code;
+
+	client = (struct qnetd_client *)data1;
+
+	result_vote = TLV_VOTE_WAIT_FOR_REPLY;
+	send_vote = 0;
+	reschedule_timer = 0;
+
+	reply_error_code = qnetd_algorithm_timer_callback(client, &reschedule_timer,
+	    &send_vote, &result_vote);
+
+	if (reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) {
+		qnetd_log(LOG_ERR, "Algorithm for client %p returned error code. "
+		    "Sending error reply.", client);
+
+		if (qnetd_client_send_err(client, 0, 0, reply_error_code) != 0) {
+			client->schedule_disconnect = 1;
+			return (0);
+		}
+
+		return (0);
+	} else {
+		qnetd_log(LOG_DEBUG, "Algorithm for client %p decided to %s timer and %s vote "
+		    "with value %s", client,
+		    (reschedule_timer ? "reschedule" : "not reschedule"),
+		    (send_vote ? "send" : "not send"),
+		    tlv_vote_to_str(result_vote));
+	}
+
+	if (send_vote) {
+		client->algo_timer_vote_info_msq_seq_number++;
+
+		if (qnetd_client_send_vote_info(client,
+		    client->algo_timer_vote_info_msq_seq_number, result_vote) != 0) {
+			client->schedule_disconnect = 1;
+			return (0);
+		}
+	}
+
+	if (reschedule_timer) {
+		/*
+		 * Timer list makes sure to schedule callback again
+		 */
+		return (-1);
+	}
+
+	client->algo_timer = NULL;
+	return (0);
+}
+
+int
+qnetd_client_algo_timer_is_scheduled(struct qnetd_client *client)
+{
+
+	return (client->algo_timer != NULL);
+}
+
+int
+qnetd_client_algo_timer_schedule_timeout(struct qnetd_client *client, uint32_t timeout)
+{
+
+	if (qnetd_client_algo_timer_is_scheduled(client)) {
+		if (qnetd_client_algo_timer_abort(client) != 0) {
+			qnetd_log(LOG_ERR, "Can't abort algo timer");
+
+			return (-1);
+		}
+	}
+
+	client->algo_timer = timer_list_add(client->main_timer_list, timeout,
+	    qnetd_client_algo_timer_callback, (void *)client, NULL);
+	if (client->algo_timer == NULL) {
+		qnetd_log(LOG_ERR, "Can't schedule algo timer");
+
+		return (-1);
+	}
+
+	return (0);
+}
+
+int
+qnetd_client_algo_timer_schedule(struct qnetd_client *client)
+{
+
+	return (qnetd_client_algo_timer_schedule_timeout(client, client->heartbeat_interval / 4));
+}
+
+int
+qnetd_client_algo_timer_abort(struct qnetd_client *client)
+{
+
+	if (qnetd_client_algo_timer_is_scheduled(client)) {
+		timer_list_delete(client->main_timer_list, client->algo_timer);
+		client->algo_timer = NULL;
+	}
+
+	return (0);
+}

+ 57 - 0
qdevices/qnetd-client-algo-timer.h

@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _QNETD_CLIENT_ALGO_TIMER_H_
+#define _QNETD_CLIENT_ALGO_TIMER_H_
+
+#include "qnetd-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	qnetd_client_algo_timer_is_scheduled(struct qnetd_client *client);
+
+extern int	qnetd_client_algo_timer_schedule_timeout(struct qnetd_client *client,
+    uint32_t timeout);
+
+extern int	qnetd_client_algo_timer_schedule(struct qnetd_client *client);
+
+extern int	qnetd_client_algo_timer_abort(struct qnetd_client *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_CLIENT_ALGO_TIMER_H_ */

+ 4 - 2
qdevices/qnetd-client-list.c

@@ -50,7 +50,8 @@ qnetd_client_list_init(struct qnetd_client_list *client_list)
 
 struct qnetd_client *
 qnetd_client_list_add(struct qnetd_client_list *client_list, PRFileDesc *sock, PRNetAddr *addr,
-	size_t max_receive_size, size_t max_send_buffers, size_t max_send_size)
+    size_t max_receive_size, size_t max_send_buffers, size_t max_send_size,
+    struct timer_list *main_timer_list)
 {
 	struct qnetd_client *client;
 
@@ -59,7 +60,8 @@ qnetd_client_list_add(struct qnetd_client_list *client_list, PRFileDesc *sock, P
 		return (NULL);
 	}
 
-	qnetd_client_init(client, sock, addr, max_receive_size, max_send_buffers, max_send_size);
+	qnetd_client_init(client, sock, addr, max_receive_size, max_send_buffers, max_send_size,
+	    main_timer_list);
 
 	TAILQ_INSERT_TAIL(client_list, client, entries);
 

+ 1 - 1
qdevices/qnetd-client-list.h

@@ -50,7 +50,7 @@ extern void			 qnetd_client_list_init(struct qnetd_client_list *client_list);
 
 extern struct qnetd_client	*qnetd_client_list_add(struct qnetd_client_list *client_list,
     PRFileDesc *sock, PRNetAddr *addr, size_t max_receive_size, size_t max_send_buffers,
-    size_t max_send_size);
+    size_t max_send_size, struct timer_list *main_timer_list);
 
 extern void			 qnetd_client_list_free(struct qnetd_client_list *client_list);
 

+ 1 - 1
qdevices/qnetd-client-net.c

@@ -208,7 +208,7 @@ qnetd_client_net_accept(struct qnetd_instance *instance)
 
 	client = qnetd_client_list_add(&instance->clients, client_socket, &client_addr,
 	    instance->max_client_receive_size, instance->max_client_send_buffers,
-	    instance->max_client_send_size);
+	    instance->max_client_send_size, &instance->main_timer_list);
 	if (client == NULL) {
 		qnetd_log(LOG_ERR, "Can't add client to list");
 

+ 5 - 1
qdevices/qnetd-client.c

@@ -38,10 +38,12 @@
 
 #include "qnet-config.h"
 #include "qnetd-client.h"
+#include "qnetd-client-algo-timer.h"
 
 void
 qnetd_client_init(struct qnetd_client *client, PRFileDesc *sock, PRNetAddr *addr,
-    size_t max_receive_size, size_t max_send_buffers, size_t max_send_size)
+    size_t max_receive_size, size_t max_send_buffers, size_t max_send_size,
+    struct timer_list *main_timer_list)
 {
 
 	memset(client, 0, sizeof(*client));
@@ -53,12 +55,14 @@ qnetd_client_init(struct qnetd_client *client, PRFileDesc *sock, PRNetAddr *addr
 	node_list_init(&client->last_membership_node_list);
 	node_list_init(&client->last_quorum_node_list);
 	client->tie_breaker.mode = QNETD_DEFAULT_TIE_BREAKER_MODE;
+	client->main_timer_list = main_timer_list;
 }
 
 void
 qnetd_client_destroy(struct qnetd_client *client)
 {
 
+	qnetd_client_algo_timer_abort(client);
 	free(client->cluster_name);
 	node_list_free(&client->last_quorum_node_list);
 	node_list_free(&client->last_membership_node_list);

+ 6 - 1
qdevices/qnetd-client.h

@@ -76,12 +76,17 @@ struct qnetd_client {
 	struct tlv_ring_id last_ring_id;
 	struct qnetd_cluster *cluster;
 	struct qnetd_cluster_list *cluster_list;
+	struct timer_list *main_timer_list;
+	struct timer_list_entry *algo_timer;
+	uint32_t algo_timer_vote_info_msq_seq_number;
+	int schedule_disconnect;
 	TAILQ_ENTRY(qnetd_client) entries;
 	TAILQ_ENTRY(qnetd_client) cluster_entries;
 };
 
 extern void		qnetd_client_init(struct qnetd_client *client, PRFileDesc *sock,
-    PRNetAddr *addr, size_t max_receive_size, size_t max_send_buffers, size_t max_send_size);
+    PRNetAddr *addr, size_t max_receive_size, size_t max_send_buffers, size_t max_send_size,
+    struct timer_list *main_timer_list);
 
 extern void		qnetd_client_destroy(struct qnetd_client *client);
 

+ 4 - 0
qdevices/qnetd-instance.c

@@ -61,6 +61,8 @@ qnetd_instance_init(struct qnetd_instance *instance, size_t max_client_receive_s
 
 	instance->max_clients = max_clients;
 
+	timer_list_init(&instance->main_timer_list);
+
 	return (0);
 }
 
@@ -70,6 +72,8 @@ qnetd_instance_destroy(struct qnetd_instance *instance)
 	struct qnetd_client *client;
 	struct qnetd_client *client_next;
 
+	timer_list_free(&instance->main_timer_list);
+
 	client = TAILQ_FIRST(&instance->clients);
 	while (client != NULL) {
 		client_next = TAILQ_NEXT(client, entries);

+ 2 - 0
qdevices/qnetd-instance.h

@@ -45,6 +45,7 @@
 #include "qnetd-cluster-list.h"
 #include "qnetd-poll-array.h"
 #include "qnet-config.h"
+#include "timer-list.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -67,6 +68,7 @@ struct qnetd_instance {
 	int tls_client_cert_required;
 	const char *host_addr;
 	uint16_t host_port;
+	struct timer_list main_timer_list;
 };
 
 extern int		qnetd_instance_init(struct qnetd_instance *instance,