1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2022 Craig Howard 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include "restart-module.h" 25 26#include <pulse/timeval.h> 27#include <pulse/xmalloc.h> 28#include <pulse/mainloop.h> 29 30#include <pulsecore/core.h> 31#include <pulsecore/thread-mq.h> 32 33struct pa_restart_data { 34 init_cb do_init; 35 done_cb do_done; 36 37 pa_usec_t restart_usec; 38 pa_module *module; 39 pa_time_event *time_event; 40 pa_defer_event *defer_event; 41}; 42 43static void do_reinit(pa_mainloop_api *mainloop, pa_restart_data *rd); 44 45static void call_init(pa_mainloop_api *mainloop, pa_time_event *e, const struct timeval *tv, void *userdata) { 46 pa_restart_data *rd = userdata; 47 int ret; 48 49 if (rd->time_event) { 50 mainloop->time_free(rd->time_event); 51 rd->time_event = NULL; 52 } 53 54 /* now that restart_usec has elapsed, we call do_init to restart the module */ 55 ret = rd->do_init(rd->module); 56 57 /* if the init failed, we got here because the caller wanted to restart, so 58 * setup another restart */ 59 if (ret < 0) 60 do_reinit(mainloop, rd); 61} 62 63static void defer_callback(pa_mainloop_api *mainloop, pa_defer_event *e, void *userdata) { 64 pa_restart_data *rd = userdata; 65 66 pa_assert(rd->defer_event == e); 67 68 mainloop->defer_enable(rd->defer_event, 0); 69 mainloop->defer_free(rd->defer_event); 70 rd->defer_event = NULL; 71 72 do_reinit(mainloop, rd); 73} 74 75static void do_reinit(pa_mainloop_api *mainloop, pa_restart_data *rd) { 76 struct timeval tv; 77 78 pa_assert_ctl_context(); 79 80 /* call do_done on the module, which will effectively tear it down; all 81 * that remains is the pa_module */ 82 rd->do_done(rd->module); 83 84 /* after restart_usec, call do_init to restart the module */ 85 pa_gettimeofday(&tv); 86 pa_timeval_add(&tv, rd->restart_usec); 87 rd->time_event = mainloop->time_new(mainloop, &tv, call_init, rd); 88} 89 90pa_restart_data *pa_restart_module_reinit(pa_module *m, init_cb do_init, done_cb do_done, pa_usec_t restart_usec) { 91 pa_restart_data *rd; 92 93 pa_assert_ctl_context(); 94 pa_assert(do_init); 95 pa_assert(do_done); 96 pa_assert(restart_usec); 97 98 pa_log_info("Starting reinit for %s", m->name); 99 100 rd = pa_xnew0(pa_restart_data, 1); 101 rd->do_init = do_init; 102 rd->do_done = do_done; 103 rd->restart_usec = restart_usec; 104 rd->module = m; 105 106 /* defer actually doing a reinit, so that we can safely exit whatever call 107 * chain we're in before we effectively reinit the module */ 108 rd->defer_event = m->core->mainloop->defer_new(m->core->mainloop, defer_callback, rd); 109 m->core->mainloop->defer_enable(rd->defer_event, 1); 110 111 return rd; 112} 113 114void pa_restart_free(pa_restart_data *rd) { 115 pa_assert_ctl_context(); 116 pa_assert(rd); 117 118 if (rd->defer_event) { 119 rd->module->core->mainloop->defer_enable(rd->defer_event, 0); 120 rd->module->core->mainloop->defer_free(rd->defer_event); 121 } 122 123 if (rd->time_event) { 124 pa_log_info("Cancel reinit for %s", rd->module->name); 125 rd->module->core->mainloop->time_free(rd->time_event); 126 } 127 128 pa_xfree(rd); 129} 130