diff options
author | Wim Taymans <wtaymans@redhat.com> | 2016-11-21 12:54:40 +0100 |
---|---|---|
committer | Wim Taymans <wtaymans@redhat.com> | 2016-11-21 12:54:40 +0100 |
commit | 3dcbf4b2283b5a4d88289d015e7305e343e7b581 (patch) | |
tree | de0b92b407fd60275732700730d0674747dd8823 | |
parent | d250ed42e6e5839471e07ef4fd78f41ce8c5ca7b (diff) |
Move suspend on idle in module
Move suspend-on-idle code from the node to a module
Add some more SpaLoop API
-rw-r--r-- | pinos/client/loop.c | 12 | ||||
-rw-r--r-- | pinos/client/loop.h | 7 | ||||
-rw-r--r-- | pinos/modules/meson.build | 9 | ||||
-rw-r--r-- | pinos/modules/module-autolink.c | 6 | ||||
-rw-r--r-- | pinos/modules/module-suspend-on-idle.c | 216 | ||||
-rw-r--r-- | pinos/server/core.c | 1 | ||||
-rw-r--r-- | pinos/server/core.h | 3 | ||||
-rw-r--r-- | pinos/server/data-loop.c | 4 | ||||
-rw-r--r-- | pinos/server/link.c | 4 | ||||
-rw-r--r-- | pinos/server/node.c | 100 | ||||
-rw-r--r-- | pinos/server/node.h | 11 | ||||
-rw-r--r-- | pinos/server/port.c | 2 | ||||
-rw-r--r-- | spa/include/spa/loop.h | 98 |
13 files changed, 363 insertions, 110 deletions
diff --git a/pinos/client/loop.c b/pinos/client/loop.c index 87d0c602..141ddef6 100644 --- a/pinos/client/loop.c +++ b/pinos/client/loop.c @@ -284,11 +284,17 @@ pinos_loop_set_hooks (PinosLoop *loop, } void -pinos_loop_set_thread (PinosLoop *loop, - void *thread) +pinos_loop_enter_thread (PinosLoop *loop) { PinosLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosLoopImpl, this); - impl->thread = *((pthread_t*)thread); + impl->thread = pthread_self(); +} + +void +pinos_loop_leave_thread (PinosLoop *loop) +{ + PinosLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosLoopImpl, this); + impl->thread = 0; } SpaResult diff --git a/pinos/client/loop.h b/pinos/client/loop.h index 6c802987..6e010e2c 100644 --- a/pinos/client/loop.h +++ b/pinos/client/loop.h @@ -30,9 +30,6 @@ extern "C" { typedef struct _PinosLoop PinosLoop; -typedef bool (*PinosCheckfunc) (PinosLoop *loop, - void *data); - typedef void (*PinosLoopHook) (PinosLoop *loop, void *data); @@ -72,8 +69,8 @@ void pinos_loop_set_hooks (PinosLoop *loop, PinosLoopHook pre_func, PinosLoopHook post_func, void *data); -void pinos_loop_set_thread (PinosLoop *loop, - void *thread); +void pinos_loop_enter_thread (PinosLoop *loop); +void pinos_loop_leave_thread (PinosLoop *loop); SpaResult pinos_loop_iterate (PinosLoop *loop, int timeout); diff --git a/pinos/modules/meson.build b/pinos/modules/meson.build index 4dbd56fc..f3b728a9 100644 --- a/pinos/modules/meson.build +++ b/pinos/modules/meson.build @@ -23,3 +23,12 @@ pinos_module_protocol_dbus = shared_library('pinos-module-protocol-dbus', [ 'mod install_dir : '@0@/pinos-0.1'.format(get_option('libdir')), dependencies : [gobject_dep, gmodule_dep, glib_dep, gio_dep, mathlib, dl_lib, pinos_dep, pinoscore_dep], ) + +pinos_module_suspend_on_idle = shared_library('pinos-module-suspend-on-idle', [ 'module-suspend-on-idle.c' ], + c_args : pinos_module_c_args, + include_directories : [configinc, pinosinc, spa_inc], + link_with : spalib, + install : true, + install_dir : '@0@/pinos-0.1'.format(get_option('libdir')), + dependencies : [mathlib, dl_lib, pinos_dep, pinoscore_dep], +) diff --git a/pinos/modules/module-autolink.c b/pinos/modules/module-autolink.c index 7a30caaf..f86e6943 100644 --- a/pinos/modules/module-autolink.c +++ b/pinos/modules/module-autolink.c @@ -96,7 +96,7 @@ try_link_port (PinosNode *node, PinosPort *port, ModuleImpl *impl) error: { - pinos_node_report_error (node, error); + pinos_node_update_state (node, PINOS_NODE_STATE_ERROR, error); return; } } @@ -128,9 +128,9 @@ on_link_state_changed (PinosListener *listener, pinos_log_debug ("module %p: link %p: state error: %s", impl, link, link->error); if (link->input && link->input->node) - pinos_node_report_error (link->input->node, strdup (link->error)); + pinos_node_update_state (link->input->node, PINOS_NODE_STATE_ERROR, strdup (link->error)); if (link->output && link->output->node) - pinos_node_report_error (link->output->node, strdup (link->error)); + pinos_node_update_state (link->output->node, PINOS_NODE_STATE_ERROR, strdup (link->error)); break; } diff --git a/pinos/modules/module-suspend-on-idle.c b/pinos/modules/module-suspend-on-idle.c new file mode 100644 index 00000000..47549349 --- /dev/null +++ b/pinos/modules/module-suspend-on-idle.c @@ -0,0 +1,216 @@ +/* Pinos + * Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include "config.h" + +#include "pinos/server/core.h" +#include "pinos/server/module.h" + +#define MODULE_URI "http://pinos.org/ns/module-suspend-on-idle" +#define MODULE_PREFIX MODULE_URI "#" + +typedef struct { + PinosCore *core; + PinosProperties *properties; + PinosGlobal *global; + + struct { + uint32_t module; + } uri; + + PinosListener global_added; + PinosListener global_removed; + PinosListener node_state_request; + PinosListener node_state_changed; + + SpaList node_list; +} ModuleImpl; + +typedef struct { + ModuleImpl *impl; + PinosNode *node; + SpaList link; + PinosSource *timeout; + guint idle_timeout; +} NodeInfo; + +static NodeInfo * +find_node_info (ModuleImpl *impl, PinosNode *node) +{ + NodeInfo *info; + + spa_list_for_each (info, &impl->node_list, link) { + if (info->node == node) + return info; + } + return NULL; +} + +static void +remove_idle_timeout (NodeInfo *info) +{ + if (info->idle_timeout) { + g_source_remove (info->idle_timeout); + info->idle_timeout = 0; + } +} + +static bool +idle_timeout (NodeInfo *info) +{ + info->idle_timeout = 0; + pinos_log_debug ("module %p: node %p idle timeout", info->impl, info->node); + pinos_node_set_state (info->node, PINOS_NODE_STATE_SUSPENDED); + return G_SOURCE_REMOVE; +} + +static void +on_node_state_request (PinosListener *listener, + PinosNode *node, + PinosNodeState state) +{ + ModuleImpl *impl = SPA_CONTAINER_OF (listener, ModuleImpl, node_state_changed); + NodeInfo *info; + + if ((info = find_node_info (impl, node)) == NULL) + return; + + remove_idle_timeout (info); +} + +static void +on_node_state_changed (PinosListener *listener, + PinosNode *node, + PinosNodeState old, + PinosNodeState state) +{ + ModuleImpl *impl = SPA_CONTAINER_OF (listener, ModuleImpl, node_state_changed); + NodeInfo *info; + + if ((info = find_node_info (impl, node)) == NULL) + return; + + if (state != PINOS_NODE_STATE_IDLE) { + remove_idle_timeout (info); + } else { + pinos_log_debug ("module %p: node %p became idle", impl, node); + info->idle_timeout = g_timeout_add_seconds (3, + (GSourceFunc) idle_timeout, + info); + } +} + +static void +on_global_added (PinosListener *listener, + PinosCore *core, + PinosGlobal *global) +{ + ModuleImpl *impl = SPA_CONTAINER_OF (listener, ModuleImpl, global_added); + + if (global->type == impl->core->registry.uri.node) { + PinosNode *node = global->object; + NodeInfo *info; + + info = calloc (1, sizeof (NodeInfo)); + info->impl = impl; + info->node = node; + info->timeout = NULL; + spa_list_insert (impl->node_list.prev, &info->link); + } +} + +static void +on_global_removed (PinosListener *listener, + PinosCore *core, + PinosGlobal *global) +{ + ModuleImpl *impl = SPA_CONTAINER_OF (listener, ModuleImpl, global_removed); + + if (global->type == impl->core->registry.uri.node) { + PinosNode *node = global->object; + NodeInfo *info; + + if ((info = find_node_info (impl, node))) { + remove_idle_timeout (info); + spa_list_remove (&info->link); + free (info); + } + } +} + + +/** + * module_new: + * @core: #PinosCore + * @properties: #PinosProperties + * + * Make a new #ModuleImpl object with given @properties + * + * Returns: a new #ModuleImpl + */ +static ModuleImpl * +module_new (PinosCore *core, + PinosProperties *properties) +{ + ModuleImpl *impl; + + impl = calloc (1, sizeof (ModuleImpl)); + pinos_log_debug ("module %p: new", impl); + + impl->core = core; + impl->properties = properties; + + spa_list_init (&impl->node_list); + + pinos_signal_add (&core->global_added, &impl->global_added, on_global_added); + pinos_signal_add (&core->global_removed, &impl->global_removed, on_global_removed); + pinos_signal_add (&core->node_state_request, &impl->node_state_request, on_node_state_request); + pinos_signal_add (&core->node_state_changed, &impl->node_state_changed, on_node_state_changed); + + impl->uri.module = spa_id_map_get_id (core->registry.map, MODULE_URI); + + impl->global = pinos_core_add_global (core, + impl->uri.module, + impl); + return impl; +} + +#if 0 +static void +module_destroy (ModuleImpl *impl) +{ + pinos_log_debug ("module %p: destroy", impl); + + pinos_global_destroy (impl->global); + + pinos_signal_remove (&impl->node_state_changed); + free (impl); +} +#endif + +bool +pinos__module_init (PinosModule * module, const char * args) +{ + module_new (module->core, NULL); + return TRUE; +} diff --git a/pinos/server/core.c b/pinos/server/core.c index 5b020a2e..57c43611 100644 --- a/pinos/server/core.c +++ b/pinos/server/core.c @@ -64,6 +64,7 @@ pinos_core_new (PinosMainLoop *main_loop) pinos_signal_init (&this->destroy_signal); pinos_signal_init (&this->global_added); pinos_signal_init (&this->global_removed); + pinos_signal_init (&this->node_state_request); pinos_signal_init (&this->node_state_changed); pinos_signal_init (&this->port_added); pinos_signal_init (&this->port_removed); diff --git a/pinos/server/core.h b/pinos/server/core.h index 88550425..b82acc28 100644 --- a/pinos/server/core.h +++ b/pinos/server/core.h @@ -76,6 +76,9 @@ struct _PinosCore { PinosCore *core, PinosGlobal *global)); + PINOS_SIGNAL (node_state_request, (PinosListener *listener, + PinosNode *object, + PinosNodeState state)); PINOS_SIGNAL (node_state_changed, (PinosListener *listener, PinosNode *object, PinosNodeState old, diff --git a/pinos/server/data-loop.c b/pinos/server/data-loop.c index f9d97def..358cbe35 100644 --- a/pinos/server/data-loop.c +++ b/pinos/server/data-loop.c @@ -96,11 +96,14 @@ do_loop (void *user_data) make_realtime (this); pinos_log_debug ("data-loop %p: enter thread", this); + pinos_loop_enter_thread (impl->this.loop); + while (impl->running) { if ((res = pinos_loop_iterate (this->loop, -1)) < 0) pinos_log_warn ("data-loop %p: iterate error %d", this, res); } pinos_log_debug ("data-loop %p: leave thread", this); + pinos_loop_leave_thread (impl->this.loop); return NULL; } @@ -169,7 +172,6 @@ pinos_data_loop_start (PinosDataLoop *loop) impl->running = false; return SPA_RESULT_ERROR; } - pinos_loop_set_thread (impl->this.loop, &impl->thread); } return SPA_RESULT_OK; } diff --git a/pinos/server/link.c b/pinos/server/link.c index a6b25960..bf9c6002 100644 --- a/pinos/server/link.c +++ b/pinos/server/link.c @@ -748,7 +748,7 @@ do_link_remove_done (SpaLoop *loop, if (this->input->node->n_used_input_links == 0 && this->input->node->n_used_output_links == 0) - pinos_node_report_idle (this->input->node); + pinos_node_set_state (this->input->node, PINOS_NODE_STATE_IDLE); clear_port_buffers (this, this->input); this->input = NULL; @@ -759,7 +759,7 @@ do_link_remove_done (SpaLoop *loop, if (this->output->node->n_used_input_links == 0 && this->output->node->n_used_output_links == 0) - pinos_node_report_idle (this->output->node); + pinos_node_set_state (this->output->node, PINOS_NODE_STATE_IDLE); clear_port_buffers (this, this->output); this->output = NULL; diff --git a/pinos/server/node.c b/pinos/server/node.c index bd20d94f..3e4a9a48 100644 --- a/pinos/server/node.c +++ b/pinos/server/node.c @@ -37,8 +37,6 @@ typedef struct uint32_t seq; bool async_init; - - guint idle_timeout; } PinosNodeImpl; static void init_complete (PinosNode *this); @@ -396,7 +394,7 @@ init_complete (PinosNode *this) pinos_log_debug ("node %p: init completed", this); impl->async_init = false; - pinos_node_update_state (this, PINOS_NODE_STATE_SUSPENDED); + pinos_node_update_state (this, PINOS_NODE_STATE_SUSPENDED, NULL); } void @@ -631,30 +629,19 @@ pinos_node_get_free_port (PinosNode *node, } static void -remove_idle_timeout (PinosNode *node) -{ - PinosNodeImpl *impl = SPA_CONTAINER_OF (node, PinosNodeImpl, this); - - if (impl->idle_timeout) { - g_source_remove (impl->idle_timeout); - impl->idle_timeout = 0; - } -} - -static void on_state_complete (PinosNode *node, gpointer data, SpaResult res) { PinosNodeState state = GPOINTER_TO_INT (data); + char *error = NULL; pinos_log_debug ("node %p: state complete %d", node, res); if (SPA_RESULT_IS_ERROR (res)) { - char *error; asprintf (&error, "error changing node state: %d", res); - pinos_node_report_error (node, error); - } else - pinos_node_update_state (node, state); + state = PINOS_NODE_STATE_ERROR; + } + pinos_node_update_state (node, state, error); } /** @@ -672,7 +659,7 @@ pinos_node_set_state (PinosNode *node, { SpaResult res = SPA_RESULT_OK; - remove_idle_timeout (node); + pinos_signal_emit (&node->core->node_state_request, node, state); pinos_log_debug ("node %p: set state %s", node, pinos_node_state_as_string (state)); @@ -715,13 +702,15 @@ pinos_node_set_state (PinosNode *node, * pinos_node_update_state: * @node: a #PinosNode * @state: a #PinosNodeState + * @error: error when @state is #PINOS_NODE_STATE_ERROR * * Update the state of a node. This method is used from * inside @node itself. */ void pinos_node_update_state (PinosNode *node, - PinosNodeState state) + PinosNodeState state, + char *error) { PinosNodeState old; @@ -731,75 +720,10 @@ pinos_node_update_state (PinosNode *node, pinos_node_state_as_string (old), pinos_node_state_as_string (state)); + if (node->error) + free (node->error); + node->error = error; node->state = state; pinos_signal_emit (&node->core->node_state_changed, node, old, state); } } - -/** - * pinos_node_report_error: - * @node: a #PinosNode - * @error: an error message - * - * Report an error from within @node. - */ -void -pinos_node_report_error (PinosNode *node, - char *error) -{ - PinosNodeState old; - - free (node->error); - remove_idle_timeout (node); - node->error = error; - old = node->state; - node->state = PINOS_NODE_STATE_ERROR; - pinos_log_debug ("node %p: got error state %s", node, error); - pinos_signal_emit (&node->core->node_state_changed, node, old, node->state); -} - -static bool -idle_timeout (PinosNode *node) -{ - PinosNodeImpl *impl = SPA_CONTAINER_OF (node, PinosNodeImpl, this); - - impl->idle_timeout = 0; - pinos_log_debug ("node %p: idle timeout", node); - pinos_node_set_state (node, PINOS_NODE_STATE_SUSPENDED); - - return G_SOURCE_REMOVE; -} - -/** - * pinos_node_report_idle: - * @node: a #PinosNode - * - * Mark @node as being idle. This will start a timeout that will - * set the node to SUSPENDED. - */ -void -pinos_node_report_idle (PinosNode *node) -{ - PinosNodeImpl *impl = SPA_CONTAINER_OF (node, PinosNodeImpl, this); - - pinos_log_debug ("node %p: report idle", node); - pinos_node_set_state (node, PINOS_NODE_STATE_IDLE); - - impl->idle_timeout = g_timeout_add_seconds (3, - (GSourceFunc) idle_timeout, - node); -} - -/** - * pinos_node_report_busy: - * @node: a #PinosNode - * - * Mark @node as being busy. This will set the state of the node - * to the RUNNING state. - */ -void -pinos_node_report_busy (PinosNode *node) -{ - pinos_log_debug ("node %p: report busy", node); - pinos_node_set_state (node, PINOS_NODE_STATE_RUNNING); -} diff --git a/pinos/server/node.h b/pinos/server/node.h index 6507779f..4947f065 100644 --- a/pinos/server/node.h +++ b/pinos/server/node.h @@ -103,12 +103,11 @@ void pinos_node_set_data_loop (PinosNode *node, PinosPort * pinos_node_get_free_port (PinosNode *node, PinosDirection direction); -SpaResult pinos_node_set_state (PinosNode *node, PinosNodeState state); -void pinos_node_update_state (PinosNode *node, PinosNodeState state); - -void pinos_node_report_error (PinosNode *node, char *error); -void pinos_node_report_idle (PinosNode *node); -void pinos_node_report_busy (PinosNode *node); +SpaResult pinos_node_set_state (PinosNode *node, + PinosNodeState state); +void pinos_node_update_state (PinosNode *node, + PinosNodeState state, + char *error); #ifdef __cplusplus } diff --git a/pinos/server/port.c b/pinos/server/port.c index 77eaa830..8700f077 100644 --- a/pinos/server/port.c +++ b/pinos/server/port.c @@ -238,7 +238,7 @@ do_remove_link_done (SpaLoop *loop, if (node->n_used_output_links == 0 && node->n_used_input_links == 0) { - pinos_node_report_idle (node); + pinos_node_update_state (node, PINOS_NODE_STATE_IDLE, NULL); } if (!port->allocated) { diff --git a/spa/include/spa/loop.h b/spa/include/spa/loop.h index 22338dc1..d661e769 100644 --- a/spa/include/spa/loop.h +++ b/spa/include/spa/loop.h @@ -26,11 +26,15 @@ extern "C" { typedef struct _SpaLoop SpaLoop; typedef struct _SpaSource SpaSource; +typedef struct _SpaLoopControl SpaLoopControl; +typedef struct _SpaLoopUtils SpaLoopUtils; #define SPA_LOOP_URI "http://spaplug.in/ns/loop" #define SPA_LOOP_PREFIX SPA_LOOP_URI "#" #define SPA_LOOP__MainLoop SPA_LOOP_PREFIX "MainLoop" #define SPA_LOOP__DataLoop SPA_LOOP_PREFIX "DataLoop" +#define SPA_LOOP__Control SPA_LOOP_PREFIX "Control" +#define SPA_LOOP__Utils SPA_LOOP_PREFIX "Utils" #include <spa/defs.h> @@ -62,7 +66,7 @@ typedef SpaResult (*SpaInvokeFunc) (SpaLoop *loop, /** * SpaLoop: * - * Register sources to an event loop + * Register sources and work items to an event loop */ struct _SpaLoop { /* the total size of this structure. This can be used to expand this @@ -88,6 +92,98 @@ struct _SpaLoop { #define spa_loop_remove_source(l,...) (l)->remove_source(__VA_ARGS__) #define spa_loop_invoke(l,...) (l)->invoke((l),__VA_ARGS__) +typedef void (*SpaLoopHook) (SpaLoopControl *ctrl, + void *data); +/** + * SpaLoopControl: + * + * Control an event loop + */ +struct _SpaLoopControl { + /* the total size of this structure. This can be used to expand this + * structure in the future */ + size_t size; + + int (*get_fd) (SpaLoopControl *ctrl); + + SpaResult (*set_hooks) (SpaLoopControl *ctrl, + SpaLoopHook pre_hook, + SpaLoopHook post_hook, + void *data); + + SpaResult (*enter) (SpaLoopControl *ctrl); + SpaResult (*leave) (SpaLoopControl *ctrl); + + SpaResult (*iterate) (SpaLoopControl *ctrl, + int timeout); +}; + +#define spa_loop_control_get_fd(l) (l)->get_fd(l) +#define spa_loop_control_set_hooks(l,...) (l)->set_hook((l),__VA_ARGS__) +#define spa_loop_control_enter(l) (l)->enter(l) +#define spa_loop_control_leave(l) (l)->leave(l) +#define spa_loop_control_iterate(l,...) (l)->iterate((l),__VA_ARGS__) + + +typedef void (*SpaSourceIOFunc) (SpaSource *source, + int fd, + SpaIO mask, + void *data); +typedef void (*SpaSourceIdleFunc) (SpaSource *source, + void *data); +typedef void (*SpaSourceEventFunc) (SpaSource *source, + void *data); +typedef void (*SpaSourceTimerFunc) (SpaSource *source, + void *data); +typedef void (*SpaSourceSignalFunc) (SpaSource *source, + int signal_number, + void *data); + +/** + * SpaLoopUtils: + * + * Create sources for an event loop + */ +struct _SpaLoopUtils { + /* the total size of this structure. This can be used to expand this + * structure in the future */ + size_t size; + + SpaSource * (*add_io) (SpaLoopUtils *utils, + int fd, + SpaIO mask, + SpaSourceIOFunc func, + void *data); + SpaResult (*update_io) (SpaSource *source, + SpaIO mask); + + SpaSource * (*add_idle) (SpaLoopUtils *utils, + SpaSourceIdleFunc func, + void *data); + SpaResult (*enable_idle) (SpaSource *source, + bool enabled); + + SpaSource * (*add_event) (SpaLoopUtils *utils, + SpaSourceEventFunc func, + void *data); + SpaResult (*signal_event) (SpaSource *source); + + SpaSource * (*add_timer) (SpaLoopUtils *utils, + SpaSourceTimerFunc func, + void *data); + SpaResult (*update_timer) (SpaSource *source, + struct timespec *value, + struct timespec *interval, + bool absolute); + SpaSource * (*add_signal) (SpaLoopUtils *utils, + int signal_number, + SpaSourceSignalFunc func, + void *data); + + SpaSource * (*destroy_source) (SpaSource *source); +}; + + #ifdef __cplusplus } /* extern "C" */ #endif |