Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the proAdhocServer lighter #19248

Open
2 tasks done
Anuskuss opened this issue Jun 4, 2024 · 4 comments
Open
2 tasks done

Make the proAdhocServer lighter #19248

Anuskuss opened this issue Jun 4, 2024 · 4 comments

Comments

@Anuskuss
Copy link
Contributor

Anuskuss commented Jun 4, 2024

What should happen

I'm running my own proAdhocServer (using this mirror) but PPSSPP suffers from the same issue, i.e. it's running in a loop which wastes CPU cycles. Right now my Adhoc server sits at 1-2% idle while lighttpd (a HTTP server) sits at a constant 0%. The only improvement that can be made with the current design is to increase the sleep (like @anr2me has already done here) but that only reduces the CPU usage to 0-0.7%.
My question is how do you actually put the thread to sleep and only wake it up once something tries to read/write from/to the socket/port (like you'd see with a lock)? I mean it has to be possible because lighttpd listens on port 80, runs at 0% AND also responds instantly. Just increasing the sleep further probably results in connection problems so I don't think that is the solution.

Who would this benefit

PPSSPP users that enable the built-in proAdhocServer. Also energy isn't free, battery life, heat etc.

Platform (if relevant)

None

Games this would be useful in

Other emulators or software with a similar feature

No response

Checklist

@Anuskuss
Copy link
Contributor Author

Anuskuss commented Jun 4, 2024

I wrote some shitty code that does what I want but I'm not confident in making those changes to PPSSPP:

--- a/src/main.c
+++ b/src/main.c
@@ -31,16 +31,19 @@
 #include <config.h>
 #include <user.h>
 #include <status.h>
+#include <unistd.h>
 
 // Server Status
-int _status = 0;
+int server;
+int status = 0;
+int users = 0;
 
 // Function Prototypes
 void interrupt(int sig);
 void enable_address_reuse(int fd);
 void change_blocking_mode(int fd, int nonblocking);
 int create_listen_socket(uint16_t port);
-int server_loop(int server);
+int server_loop();
 
 /**
  * Server Entry Point
@@ -63,7 +66,7 @@ int main(int argc, char * argv[])
 	signal(SIGTERM, interrupt);
 	
 	// Create Listening Socket
-	int server = create_listen_socket(SERVER_PORT);
+	server = create_listen_socket(SERVER_PORT);
 	
 	// Created Listening Socket
 	if(server != -1)
@@ -72,7 +75,7 @@ int main(int argc, char * argv[])
 		printf("Listening for Connections on TCP Port %u.\n", SERVER_PORT);
 		
 		// Enter Server Loop
-		result = server_loop(server);
+		result = server_loop();
 		
 		// Notify User
 		printf("Shutdown complete.\n");
@@ -92,7 +95,8 @@ void interrupt(int sig)
 	printf("Shutting down... please wait.\n");
 	
 	// Trigger Shutdown
-	_status = 0;
+	status = 0;
+	close(server);
 }
 
 /**
@@ -146,7 +150,7 @@ int create_listen_socket(uint16_t port)
 		enable_address_reuse(fd);
 		
 		// Make Socket Nonblocking
-		change_blocking_mode(fd, 1);
+		//change_blocking_mode(fd, 1);
 		
 		// Prepare Local Address Information
 		struct sockaddr_in local;
@@ -187,22 +191,22 @@ int create_listen_socket(uint16_t port)
  * @param server Server Listening Socket
  * @return OS Error Code
  */
-int server_loop(int server)
+int server_loop()
 {
 	// Set Running Status
-	_status = 1;
+	status = 1;
 	
 	// Create Empty Status Logfile
 	update_status();
 	
 	// Handling Loop
-	while(_status == 1)
+	while(status == 1)
 	{
 		// Login Block
 		{
 			// Login Result
 			int loginresult = 0;
-			
+
 			// Login Processing Loop
 			do
 			{
@@ -213,7 +217,7 @@ int server_loop(int server)
 				
 				// Accept Login Requests
 				// loginresult = accept4(server, (struct sockaddr *)&addr, &addrlen, SOCK_NONBLOCK);
-				
+
 				// Alternative Accept Approach (some Linux Kernel don't support the accept4 Syscall... wtf?)
 				loginresult = accept(server, (struct sockaddr *)&addr, &addrlen);
 				if(loginresult != -1)
@@ -223,7 +227,11 @@ int server_loop(int server)
 				}
 				
 				// Login User (Stream)
-				if(loginresult != -1) login_user_stream(loginresult, addr.sin_addr.s_addr);
+				if(loginresult != -1) {
+					users++;
+					change_blocking_mode(server, 1);
+					login_user_stream(loginresult, addr.sin_addr.s_addr);
+				}
 			} while(loginresult != -1);
 		}
 		
@@ -241,6 +249,7 @@ int server_loop(int server)
 			if(recvresult == 0 || (recvresult == -1 && errno != EAGAIN && errno != EWOULDBLOCK) || get_user_state(user) == USER_STATE_TIMED_OUT)
 			{
 				// Logout User
+				if (--users == 0) change_blocking_mode(server, 0);
 				logout_user(user);
 			}
 			
@@ -285,6 +294,7 @@ int server_loop(int server)
 						printf("Invalid Opcode 0x%02X in Waiting State from %u.%u.%u.%u.\n", user->rx[0], ip[0], ip[1], ip[2], ip[3]);
 						
 						// Logout User
+						if (--users == 0) change_blocking_mode(server, 0);
 						logout_user(user);
 					}
 				}
@@ -369,6 +379,7 @@ int server_loop(int server)
 						printf("Invalid Opcode 0x%02X in Logged-In State from %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u).\n", user->rx[0], (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3]);
 
 						// Logout User
+						if (--users == 0) change_blocking_mode(server, 0);
 						logout_user(user);
 					}
 				}
--- a/src/user.c
+++ b/src/user.c
@@ -25,6 +25,8 @@
 #include <status.h>
 #include <config.h>
 #include <sqlite3.h>
+#include <unistd.h>
+#include <sys/socket.h>
 
 // User Count
 uint32_t _db_user_count = 0;
@anr2me
Copy link
Collaborator

anr2me commented Jun 4, 2024

Increasing the loop delay will only slowdown the response time.

What you need to do is to use blocking socket and use select to detect data availability/readability and respond to it, since select will blocks the thread more efficiently than sleep, but you will probably need to use sleep for about 1~10ms too on each loop, in case there are a lot of incoming data (ie. if you have a lot of active players) making select unable to block the thread until there is no data left (where sleep will let your CPU to rest a bit during high traffic but also gives extra response delay).

PS: as i remembered select may have different behavior/returned error code on different platform, so make sure you've tested it on all platform if the changes are for PPSSPP built-in server (probably only need to test Windows, Linux, and MacOS)

PPS: there is also poll/epoll that are recommended for modern app, but not all platforms support it, thus will also need to use select as a fallback for unsupported platforms.

@Anuskuss
Copy link
Contributor Author

Anuskuss commented Jun 5, 2024

Well I'm not a network engineer so in the end I decided to just leave accept blocking when no users are logged in, non-blocking as soon as somebody joins (change_blocking_mode(server,1)) and then blocking again once all players left (change_blocking_mode(server,0)). I'm sure you'll come up with something less awful though ;)

@anr2me
Copy link
Collaborator

anr2me commented Jun 5, 2024

Well since it's running on it's own thread it will probably safe to use blocking mode, i don't remembered much why it use non-blocking mode tho :) may be it won't be able to respond for another player's communication while blocking the thread waiting for one player's data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants