Client: use TimeoutMonitor to track connection timeout
Don't use a global loop over the whole client list.
This commit is contained in:
		| @@ -19,9 +19,6 @@ | ||||
|  | ||||
| #include "config.h" | ||||
| #include "ClientInternal.hxx" | ||||
| #include "ClientList.hxx" | ||||
|  | ||||
| static guint expire_source_id; | ||||
|  | ||||
| void | ||||
| Client::SetExpired() | ||||
| @@ -29,54 +26,18 @@ Client::SetExpired() | ||||
| 	if (IsExpired()) | ||||
| 		return; | ||||
|  | ||||
| 	client_schedule_expire(); | ||||
| 	BufferedSocket::Close(); | ||||
| 	TimeoutMonitor::Schedule(0); | ||||
| } | ||||
|  | ||||
| static void | ||||
| client_check_expired_callback(Client *client, G_GNUC_UNUSED gpointer user_data) | ||||
| bool | ||||
| Client::OnTimeout() | ||||
| { | ||||
| 	if (client->IsExpired()) { | ||||
| 		g_debug("[%u] expired", client->num); | ||||
| 		client->Close(); | ||||
| 	} else if (!client->idle_waiting && /* idle clients | ||||
| 					       never expire */ | ||||
| 		   (int)g_timer_elapsed(client->last_activity, NULL) > | ||||
| 		   client_timeout) { | ||||
| 		g_debug("[%u] timeout", client->num); | ||||
| 		client->Close(); | ||||
| 	if (!IsExpired()) { | ||||
| 		assert(!idle_waiting); | ||||
| 		g_debug("[%u] timeout", num); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void | ||||
| client_manager_expire(void) | ||||
| { | ||||
| 	client_list_foreach(client_check_expired_callback, NULL); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * An idle event which calls client_manager_expire(). | ||||
|  */ | ||||
| static gboolean | ||||
| client_manager_expire_event(G_GNUC_UNUSED gpointer data) | ||||
| { | ||||
| 	expire_source_id = 0; | ||||
| 	client_manager_expire(); | ||||
| 	Close(); | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| void | ||||
| client_schedule_expire(void) | ||||
| { | ||||
| 	if (expire_source_id == 0) | ||||
| 		/* delayed deletion */ | ||||
| 		expire_source_id = g_idle_add(client_manager_expire_event, | ||||
| 					      NULL); | ||||
| } | ||||
|  | ||||
| void | ||||
| client_deinit_expire(void) | ||||
| { | ||||
| 	if (expire_source_id != 0) | ||||
| 		g_source_remove(expire_source_id); | ||||
| } | ||||
|   | ||||
| @@ -69,6 +69,4 @@ void client_manager_deinit(void) | ||||
| 	client_close_all(); | ||||
|  | ||||
| 	client_max_connections = 0; | ||||
|  | ||||
| 	client_deinit_expire(); | ||||
| } | ||||
|   | ||||
| @@ -43,7 +43,8 @@ Client::IdleNotify() | ||||
| 	} | ||||
|  | ||||
| 	client_puts(this, "OK\n"); | ||||
| 	g_timer_start(last_activity); | ||||
|  | ||||
| 	TimeoutMonitor::ScheduleSeconds(client_timeout); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -83,6 +84,9 @@ Client::IdleWait(unsigned flags) | ||||
| 	if (idle_flags & idle_subscriptions) { | ||||
| 		IdleNotify(); | ||||
| 		return true; | ||||
| 	} else | ||||
| 	} else { | ||||
| 		/* disable timeouts while in "idle" */ | ||||
| 		TimeoutMonitor::Cancel(); | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -25,6 +25,7 @@ | ||||
| #include "ClientMessage.hxx" | ||||
| #include "CommandListBuilder.hxx" | ||||
| #include "event/BufferedSocket.hxx" | ||||
| #include "event/TimeoutMonitor.hxx" | ||||
| #include "command.h" | ||||
|  | ||||
| #include <set> | ||||
| @@ -43,7 +44,7 @@ enum { | ||||
|  | ||||
| struct Partition; | ||||
|  | ||||
| class Client final : private BufferedSocket { | ||||
| class Client final : private BufferedSocket, TimeoutMonitor { | ||||
| public: | ||||
| 	Partition &partition; | ||||
| 	struct playlist &playlist; | ||||
| @@ -54,11 +55,6 @@ public: | ||||
| 	/** the uid of the client process, or -1 if unknown */ | ||||
| 	int uid; | ||||
|  | ||||
| 	/** | ||||
| 	 * How long since the last activity from this client? | ||||
| 	 */ | ||||
| 	GTimer *last_activity; | ||||
|  | ||||
| 	CommandListBuilder cmd_list; | ||||
|  | ||||
| 	unsigned int num;	/* client number */ | ||||
| @@ -91,7 +87,6 @@ public: | ||||
|  | ||||
| 	Client(EventLoop &loop, Partition &partition, | ||||
| 	       int fd, int uid, int num); | ||||
| 	~Client(); | ||||
|  | ||||
| 	bool IsConnected() const { | ||||
| 		return BufferedSocket::IsDefined(); | ||||
| @@ -125,6 +120,9 @@ private: | ||||
| 					  size_t length) override; | ||||
| 	virtual void OnSocketError(GError *error) override; | ||||
| 	virtual void OnSocketClosed() override; | ||||
|  | ||||
| 	/* virtual methods from class TimeoutMonitor */ | ||||
| 	virtual bool OnTimeout() override; | ||||
| }; | ||||
|  | ||||
| extern unsigned int client_max_connections; | ||||
| @@ -132,19 +130,6 @@ extern int client_timeout; | ||||
| extern size_t client_max_command_list_size; | ||||
| extern size_t client_max_output_buffer_size; | ||||
|  | ||||
| /** | ||||
|  * Schedule an "expired" check for all clients: permanently delete | ||||
|  * clients which have been set "expired" with client_set_expired(). | ||||
|  */ | ||||
| void | ||||
| client_schedule_expire(void); | ||||
|  | ||||
| /** | ||||
|  * Removes a scheduled "expired" check. | ||||
|  */ | ||||
| void | ||||
| client_deinit_expire(void); | ||||
|  | ||||
| enum command_return | ||||
| client_read(Client *client); | ||||
|  | ||||
|   | ||||
| @@ -49,20 +49,16 @@ static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n"; | ||||
| Client::Client(EventLoop &_loop, Partition &_partition, | ||||
| 	       int _fd, int _uid, int _num) | ||||
| 	:BufferedSocket(_fd, _loop, 16384, client_max_output_buffer_size), | ||||
| 	 TimeoutMonitor(_loop), | ||||
| 	 partition(_partition), | ||||
| 	 playlist(partition.playlist), player_control(&partition.pc), | ||||
| 	 permission(getDefaultPermissions()), | ||||
| 	 uid(_uid), | ||||
| 	 last_activity(g_timer_new()), | ||||
| 	 num(_num), | ||||
| 	 idle_waiting(false), idle_flags(0), | ||||
| 	 num_subscriptions(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| Client::~Client() | ||||
| { | ||||
| 	g_timer_destroy(last_activity); | ||||
| 	TimeoutMonitor::ScheduleSeconds(client_timeout); | ||||
| } | ||||
|  | ||||
| void | ||||
|   | ||||
| @@ -28,13 +28,13 @@ | ||||
| BufferedSocket::InputResult | ||||
| Client::OnSocketInput(const void *data, size_t length) | ||||
| { | ||||
| 	g_timer_start(last_activity); | ||||
|  | ||||
| 	const char *p = (const char *)data; | ||||
| 	const char *newline = (const char *)memchr(p, '\n', length); | ||||
| 	if (newline == NULL) | ||||
| 		return InputResult::MORE; | ||||
|  | ||||
| 	TimeoutMonitor::ScheduleSeconds(client_timeout); | ||||
|  | ||||
| 	char *line = g_strndup(p, newline - p); | ||||
| 	BufferedSocket::ConsumeInput(newline + 1 - p); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann