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.
This commit is contained in:
Christian Kellner 2019-07-01 17:53:45 +02:00
parent 2027e981e6
commit 1a863f32a1

View File

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