Browse Source

daemon: add ref-counting to GameModeClient

This is so it can out-live its membership in the client list, e.g.
when it is passed outside of gamemode-context.c and the reaper
comes along and reaps a client in the background but we still are
using the struct outside.
Christian Kellner 5 years ago
parent
commit
1a863f32a1
1 changed files with 12 additions and 7 deletions
  1. 12 7
      daemon/gamemode-context.c

+ 12 - 7
daemon/gamemode-context.c

@@ -52,6 +52,7 @@ POSSIBILITY OF SUCH DAMAGE.
  * form to contain the pid and credentials.
  */
 struct GameModeClient {
+	_Atomic int refcount;        /**<Allow outside usage */
 	pid_t pid;                   /**< Process ID */
 	struct GameModeClient *next; /**<Next client in the list */
 	char executable[PATH_MAX];   /**<Process executable */
@@ -86,7 +87,7 @@ static GameModeContext instance = { 0 };
 static volatile bool had_context_init = false;
 
 static GameModeClient *game_mode_client_new(pid_t pid, char *exe);
-static void game_mode_client_free(GameModeClient *client);
+static void game_mode_client_unref(GameModeClient *client);
 static const GameModeClient *game_mode_context_has_client(GameModeContext *self, pid_t client);
 static void *game_mode_context_reaper(void *userdata);
 static void game_mode_context_enter(GameModeContext *self);
@@ -159,7 +160,7 @@ void game_mode_context_destroy(GameModeContext *self)
 	}
 
 	had_context_init = false;
-	game_mode_client_free(self->client);
+	game_mode_client_unref(self->client);
 
 	end_reaper_thread(self);
 
@@ -444,7 +445,7 @@ error_cleanup:
 	if (errno != 0)
 		LOG_ERROR("Failed to register client [%d]: %s\n", client, strerror(errno));
 	free(executable);
-	game_mode_client_free(cl);
+	game_mode_client_unref(cl);
 	return err;
 }
 
@@ -508,7 +509,7 @@ int game_mode_context_unregister(GameModeContext *self, pid_t client, pid_t requ
 			self->client = cl->next;
 		}
 		cl->next = NULL;
-		game_mode_client_free(cl);
+		game_mode_client_unref(cl);
 		break;
 	}
 
@@ -616,20 +617,24 @@ static GameModeClient *game_mode_client_new(pid_t pid, char *executable)
 		return NULL;
 	}
 	*ret = c;
+	ret->refcount = ATOMIC_VAR_INIT(1);
 	strncpy(ret->executable, executable, PATH_MAX - 1);
 	return ret;
 }
 
 /**
- * Free a client and the next element in the list.
+ * Unref a client and the next element in the list, if non-null.
  */
-static void game_mode_client_free(GameModeClient *client)
+static void game_mode_client_unref(GameModeClient *client)
 {
 	if (!client) {
 		return;
 	}
+	if (atomic_fetch_sub_explicit(&client->refcount, 1, memory_order_seq_cst) > 1) {
+		return; /* object is still alive */
+	}
 	if (client->next) {
-		game_mode_client_free(client->next);
+		game_mode_client_unref(client->next);
 	}
 	free(client);
 }