Use refcounting for async shutdown

fix some memory leaks
This commit is contained in:
Wim Taymans 2017-01-12 14:57:07 +01:00
parent ee0aa6a2ac
commit 6d4db64767
21 changed files with 216 additions and 260 deletions

View file

@ -119,6 +119,7 @@ typedef struct
SpaProxy proxy;
PinosListener node_free;
PinosListener transport_changed;
PinosListener loop_changed;
PinosListener global_added;
@ -177,6 +178,9 @@ spa_proxy_node_send_command (SpaNode *node,
this = SPA_CONTAINER_OF (node, SpaProxy, node);
if (this->resource == NULL)
return SPA_RESULT_OK;
switch (command->type) {
case SPA_NODE_COMMAND_INVALID:
return SPA_RESULT_INVALID_COMMAND;
@ -200,7 +204,6 @@ spa_proxy_node_send_command (SpaNode *node,
uint8_t cmd = PINOS_TRANSPORT_CMD_NEED_DATA;
write (this->data_source.fd, &cmd, 1);
}
res = SPA_RESULT_RETURN_ASYNC (cnc.seq);
break;
}
@ -295,7 +298,7 @@ spa_proxy_node_get_port_ids (SpaNode *node,
}
static void
do_update_port (SpaProxy *this,
do_update_port (SpaProxy *this,
PinosMessagePortUpdate *pu)
{
SpaProxyPort *port;
@ -312,17 +315,22 @@ do_update_port (SpaProxy *this,
for (i = 0; i < port->n_formats; i++)
free (port->formats[i]);
port->n_formats = pu->n_possible_formats;
port->formats = realloc (port->formats, port->n_formats * sizeof (SpaFormat *));
if (port->n_formats)
port->formats = realloc (port->formats, port->n_formats * sizeof (SpaFormat *));
else {
free (port->formats);
port->formats = NULL;
}
for (i = 0; i < port->n_formats; i++) {
size = pinos_serialize_format_get_size (pu->possible_formats[i]);
port->formats[i] = pinos_serialize_format_copy_into (malloc (size), pu->possible_formats[i]);
port->formats[i] = size ? pinos_serialize_format_copy_into (malloc (size), pu->possible_formats[i]) : NULL;
}
}
if (pu->change_mask & PINOS_MESSAGE_PORT_UPDATE_FORMAT) {
if (port->format)
free (port->format);
size = pinos_serialize_format_get_size (pu->format);
port->format = pinos_serialize_format_copy_into (malloc (size), pu->format);
port->format = size ? pinos_serialize_format_copy_into (malloc (size), pu->format) : NULL;
}
if (pu->change_mask & PINOS_MESSAGE_PORT_UPDATE_PROPS) {
@ -332,7 +340,7 @@ do_update_port (SpaProxy *this,
if (port->info)
free (port->info);
size = pinos_serialize_port_info_get_size (pu->info);
port->info = pinos_serialize_port_info_copy_into (malloc (size), pu->info);
port->info = size ? pinos_serialize_port_info_copy_into (malloc (size), pu->info) : NULL;
}
if (!port->valid) {
@ -482,6 +490,9 @@ spa_proxy_node_port_set_format (SpaNode *node,
if (!CHECK_PORT (this, direction, port_id))
return SPA_RESULT_INVALID_PORT;
if (this->resource == NULL)
return SPA_RESULT_OK;
sf.seq = this->seq++;
sf.direction = direction;
sf.port_id = port_id;
@ -641,6 +652,11 @@ spa_proxy_node_port_use_buffers (SpaNode *node,
mb = NULL;
}
port->n_buffers = n_buffers;
if (this->resource == NULL)
return SPA_RESULT_OK;
n_mem = 0;
for (i = 0; i < n_buffers; i++) {
ProxyBuffer *b = &port->buffers[i];
@ -715,8 +731,6 @@ spa_proxy_node_port_use_buffers (SpaNode *node,
}
}
port->n_buffers = n_buffers;
ub.seq = this->seq++;
ub.direction = direction;
ub.port_id = port_id;
@ -1169,6 +1183,9 @@ on_transport_changed (PinosListener *listener,
PinosTransportInfo info;
PinosMessageTransportUpdate tu;
if (this->resource == NULL)
return;
pinos_transport_get_info (node->transport, &info);
tu.memfd = info.memfd;
@ -1194,6 +1211,7 @@ on_global_added (PinosListener *listener,
PinosGlobal *global)
{
PinosClientNodeImpl *impl = SPA_CONTAINER_OF (listener, PinosClientNodeImpl, global_added);
if (global->object == impl->this.node)
global->owner = impl->this.client;
}
@ -1222,7 +1240,25 @@ proxy_clear (SpaProxy *this)
static void
client_node_resource_destroy (PinosResource *resource)
{
pinos_client_node_destroy (resource->object);
PinosClientNode *this = resource->object;
PinosClientNodeImpl *impl = SPA_CONTAINER_OF (this, PinosClientNodeImpl, this);
impl->proxy.resource = this->resource = NULL;
pinos_client_node_destroy (this);
}
static void
on_node_free (PinosListener *listener,
PinosNode *node)
{
PinosClientNodeImpl *impl = SPA_CONTAINER_OF (listener, PinosClientNodeImpl, node_free);
pinos_log_debug ("client-node %p: free", &impl->this);
proxy_clear (&impl->proxy);
if (impl->data_fd != -1)
close (impl->data_fd);
free (impl);
}
/**
@ -1249,6 +1285,8 @@ pinos_client_node_new (PinosClient *client,
return NULL;
this = &impl->this;
this->client = client;
impl->core = client->core;
impl->data_fd = -1;
pinos_log_debug ("client-node %p: new", impl);
@ -1277,6 +1315,10 @@ pinos_client_node_new (PinosClient *client,
impl->proxy.resource = this->resource;
pinos_signal_add (&this->node->free_signal,
&impl->node_free,
on_node_free);
pinos_signal_add (&this->node->transport_changed,
&impl->transport_changed,
on_transport_changed);
@ -1303,24 +1345,6 @@ error_no_node:
return NULL;
}
static void
on_node_remove (void *object,
void *data,
SpaResult res,
uint32_t id)
{
PinosClientNode *this = object;
PinosClientNodeImpl *impl = SPA_CONTAINER_OF (this, PinosClientNodeImpl, this);
pinos_log_debug ("client-node %p: finalize", this);
proxy_clear (&impl->proxy);
if (impl->data_fd != -1)
close (impl->data_fd);
free (impl);
}
void
pinos_client_node_destroy (PinosClientNode * this)
{
@ -1330,14 +1354,10 @@ pinos_client_node_destroy (PinosClientNode * this)
pinos_signal_emit (&this->destroy_signal, this);
pinos_signal_remove (&impl->global_added);
pinos_signal_remove (&impl->loop_changed);
pinos_signal_remove (&impl->transport_changed);
pinos_node_destroy (this->node);
pinos_main_loop_defer (this->node->core->main_loop,
this,
SPA_RESULT_WAIT_SYNC,
(PinosDeferFunc) on_node_remove,
this);
}
/**

View file

@ -110,6 +110,7 @@ no_mem:
*/
PinosClient *
pinos_client_new (PinosCore *core,
struct ucred *ucred,
PinosProperties *properties)
{
PinosClient *this;
@ -123,6 +124,8 @@ pinos_client_new (PinosCore *core,
this = &impl->this;
this->core = core;
if ((this->ucred_valid = (ucred != NULL)))
this->ucred = *ucred;
this->properties = properties;
spa_list_init (&this->resource_list);
@ -142,23 +145,6 @@ pinos_client_new (PinosCore *core,
return this;
}
static void
sync_destroy (void *object,
void *data,
SpaResult res,
uint32_t id)
{
PinosClientImpl *impl = SPA_CONTAINER_OF (object, PinosClientImpl, this);
PinosClient *client = &impl->this;
pinos_log_debug ("client %p: sync destroy", impl);
if (client->properties)
pinos_properties_free (client->properties);
free (impl);
}
static void
destroy_resource (void *object,
void *data)
@ -176,23 +162,26 @@ void
pinos_client_destroy (PinosClient * client)
{
PinosResource *resource, *tmp;
PinosClientImpl *impl = SPA_CONTAINER_OF (client, PinosClientImpl, this);
pinos_log_debug ("client %p: destroy", client);
pinos_signal_emit (&client->destroy_signal, client);
pinos_map_for_each (&client->objects, destroy_resource, client);
pinos_global_destroy (client->global);
spa_list_remove (&client->link);
pinos_global_destroy (client->global);
spa_list_for_each_safe (resource, tmp, &client->resource_list, link)
pinos_resource_destroy (resource);
pinos_main_loop_defer (client->core->main_loop,
client,
SPA_RESULT_WAIT_SYNC,
sync_destroy,
client);
pinos_map_for_each (&client->objects, destroy_resource, client);
pinos_log_debug ("client %p: free", impl);
pinos_map_clear (&client->objects);
if (client->properties)
pinos_properties_free (client->properties);
free (impl);
}
void

View file

@ -62,10 +62,11 @@ struct _PinosClient {
SpaList resource_list;
PINOS_SIGNAL (destroy_signal, (PinosListener *listener,
PinosClient *client));
PinosClient *client));
};
PinosClient * pinos_client_new (PinosCore *core,
struct ucred *ucred,
PinosProperties *properties);
void pinos_client_destroy (PinosClient *client);

View file

@ -192,6 +192,13 @@ no_mem:
return SPA_RESULT_NO_MEMORY;
}
static void
core_unbind_func (void *data)
{
PinosResource *resource = data;
resource->client->core_resource = NULL;
}
static SpaResult
core_bind_func (PinosGlobal *global,
PinosClient *client,
@ -207,7 +214,7 @@ core_bind_func (PinosGlobal *global,
id,
global->type,
global->object,
NULL);
core_unbind_func);
if (resource == NULL)
goto no_mem;
@ -310,10 +317,14 @@ pinos_core_destroy (PinosCore *core)
{
PinosCoreImpl *impl = SPA_CONTAINER_OF (core, PinosCoreImpl, this);
pinos_log_debug ("core %p: destroy", core);
pinos_signal_emit (&core->destroy_signal, core);
pinos_data_loop_destroy (core->data_loop);
pinos_map_clear (&core->objects);
pinos_log_debug ("core %p: free", core);
free (impl);
}
@ -350,10 +361,11 @@ pinos_core_add_global (PinosCore *core,
spa_list_insert (core->global_list.prev, &this->link);
pinos_signal_emit (&core->global_added, core, this);
pinos_log_debug ("global %p: new %u", this, this->id);
ng.id = this->id;
ng.type = spa_id_map_get_uri (core->uri.map, this->type);
pinos_log_debug ("global %p: new %u %s", this, ng.id, ng.type);
spa_list_for_each (registry, &core->registry_resource_list, link) {
pinos_resource_send_message (registry,
PINOS_MESSAGE_NOTIFY_GLOBAL,
@ -384,18 +396,6 @@ pinos_global_bind (PinosGlobal *global,
return res;
}
static void
sync_destroy (void *object,
void *data,
SpaResult res,
uint32_t id)
{
PinosGlobal *global = object;
pinos_log_debug ("global %p: sync destroy", global);
free (global);
}
void
pinos_global_destroy (PinosGlobal *global)
{
@ -406,11 +406,6 @@ pinos_global_destroy (PinosGlobal *global)
pinos_log_debug ("global %p: destroy %u", global, global->id);
pinos_signal_emit (&global->destroy_signal, global);
pinos_map_remove (&core->objects, global->id);
spa_list_remove (&global->link);
pinos_signal_emit (&core->global_removed, core, global);
ng.id = global->id;
spa_list_for_each (registry, &core->registry_resource_list, link) {
pinos_resource_send_message (registry,
@ -419,11 +414,13 @@ pinos_global_destroy (PinosGlobal *global)
true);
}
pinos_main_loop_defer (core->main_loop,
global,
SPA_RESULT_WAIT_SYNC,
sync_destroy,
global);
pinos_map_remove (&core->objects, global->id);
spa_list_remove (&global->link);
pinos_signal_emit (&core->global_removed, core, global);
pinos_log_debug ("global %p: free", global);
free (global);
}
PinosPort *

View file

@ -32,6 +32,8 @@ typedef struct
{
PinosLink this;
int refcount;
uint32_t seq;
SpaFormat **format_filter;
@ -648,25 +650,12 @@ on_output_async_complete_notify (PinosListener *listener,
pinos_main_loop_defer_complete (this->core->main_loop, impl, seq, res);
}
static void
on_port_unlinked (PinosPort *port, PinosLink *this, SpaResult res, uint32_t id)
{
pinos_signal_emit (&this->core->port_unlinked, this, port);
if (this->input == NULL || this->output == NULL) {
pinos_link_update_state (this, PINOS_LINK_STATE_UNLINKED, NULL);
pinos_link_destroy (this);
}
}
static void
on_port_destroy (PinosLink *this,
PinosPort *port)
{
PinosLinkImpl *impl = (PinosLinkImpl *) this;
PinosPort *other;
SpaResult res = SPA_RESULT_OK;
if (port == this->input) {
pinos_log_debug ("link %p: input port destroyed %p", this, port);
@ -691,11 +680,10 @@ on_port_destroy (PinosLink *this,
pinos_port_clear_buffers (other);
}
pinos_main_loop_defer (this->core->main_loop,
port,
res,
(PinosDeferFunc) on_port_unlinked,
this);
pinos_signal_emit (&this->core->port_unlinked, this, port);
pinos_link_update_state (this, PINOS_LINK_STATE_UNLINKED, NULL);
pinos_link_destroy (this);
}
static void
@ -749,11 +737,31 @@ link_dispatch_func (void *object,
return SPA_RESULT_OK;
}
static void
pinos_link_free (PinosLink *link)
{
PinosLinkImpl *impl = SPA_CONTAINER_OF (link, PinosLinkImpl, this);
pinos_log_debug ("link %p: free", link);
pinos_signal_emit (&link->free_signal, link);
if (impl->allocated)
pinos_memblock_free (&impl->buffer_mem);
free (impl);
}
static void
link_unbind_func (void *data)
{
PinosResource *resource = data;
PinosLink *this = resource->object;
PinosLinkImpl *impl = SPA_CONTAINER_OF (this, PinosLinkImpl, this);
spa_list_remove (&resource->link);
if (--impl->refcount == 0)
pinos_link_free (this);
}
static SpaResult
@ -763,6 +771,7 @@ link_bind_func (PinosGlobal *global,
uint32_t id)
{
PinosLink *this = global->object;
PinosLinkImpl *impl = SPA_CONTAINER_OF (this, PinosLinkImpl, this);
PinosResource *resource;
PinosMessageLinkInfo m;
PinosLinkInfo info;
@ -775,6 +784,8 @@ link_bind_func (PinosGlobal *global,
if (resource == NULL)
goto no_mem;
impl->refcount++;
pinos_resource_set_dispatch (resource,
link_dispatch_func,
global);
@ -790,6 +801,7 @@ link_bind_func (PinosGlobal *global,
info.output_port_id = this->output ? this->output->port_id : -1;
info.input_node_id = this->input ? this->input->node->global->id : -1;
info.input_port_id = this->input ? this->input->port_id : -1;
return pinos_resource_send_message (resource,
PINOS_MESSAGE_LINK_INFO,
&m,
@ -821,12 +833,14 @@ pinos_link_new (PinosCore *core,
this->core = core;
this->properties = properties;
this->state = PINOS_LINK_STATE_INIT;
impl->refcount = 1;
this->input = input;
this->output = output;
spa_list_init (&this->resource_list);
pinos_signal_init (&this->destroy_signal);
pinos_signal_init (&this->free_signal);
impl->format_filter = format_filter;
@ -884,6 +898,7 @@ do_link_remove_done (SpaLoop *loop,
void *user_data)
{
PinosLink *this = user_data;
PinosLinkImpl *impl = SPA_CONTAINER_OF (this, PinosLinkImpl, this);
if (this->input) {
spa_list_remove (&this->input_link);
@ -907,11 +922,8 @@ do_link_remove_done (SpaLoop *loop,
clear_port_buffers (this, this->output);
this->output = NULL;
}
pinos_main_loop_defer_complete (this->core->main_loop,
this,
seq,
SPA_RESULT_OK);
if (--impl->refcount == 0)
pinos_link_free (this);
return SPA_RESULT_OK;
}
@ -945,22 +957,6 @@ do_link_remove (SpaLoop *loop,
return res;
}
static void
sync_destroy (void *object,
void *data,
SpaResult res,
uint32_t id)
{
PinosLinkImpl *impl = SPA_CONTAINER_OF (object, PinosLinkImpl, this);
pinos_log_debug ("link %p: sync destroy", impl);
if (impl->allocated)
pinos_memblock_free (&impl->buffer_mem);
free (impl);
}
/**
* pinos_link_destroy:
* @link: a #PinosLink
@ -970,7 +966,6 @@ sync_destroy (void *object,
void
pinos_link_destroy (PinosLink * this)
{
SpaResult res = SPA_RESULT_OK;
PinosLinkImpl *impl = SPA_CONTAINER_OF (this, PinosLinkImpl, this);
PinosResource *resource, *tmp;
@ -987,31 +982,26 @@ pinos_link_destroy (PinosLink * this)
pinos_signal_remove (&impl->input_port_destroy);
pinos_signal_remove (&impl->input_async_complete);
res = pinos_loop_invoke (this->input->node->data_loop->loop,
do_link_remove,
impl->seq++,
0,
NULL,
this);
pinos_main_loop_defer (this->core->main_loop, this, res, NULL, NULL);
impl->refcount++;
pinos_loop_invoke (this->input->node->data_loop->loop,
do_link_remove,
impl->seq++,
0,
NULL,
this);
}
if (this->output) {
pinos_signal_remove (&impl->output_port_destroy);
pinos_signal_remove (&impl->output_async_complete);
res = pinos_loop_invoke (this->output->node->data_loop->loop,
do_link_remove,
impl->seq++,
0,
NULL,
this);
pinos_main_loop_defer (this->core->main_loop, this, res, NULL, NULL);
impl->refcount++;
pinos_loop_invoke (this->output->node->data_loop->loop,
do_link_remove,
impl->seq++,
0,
NULL,
this);
}
pinos_main_loop_defer (this->core->main_loop,
this,
SPA_RESULT_WAIT_SYNC,
sync_destroy,
this);
if (--impl->refcount == 0)
pinos_link_free (this);
}

View file

@ -51,6 +51,8 @@ struct _PinosLink {
PINOS_SIGNAL (destroy_signal, (PinosListener *,
PinosLink *));
PINOS_SIGNAL (free_signal, (PinosListener *,
PinosLink *));
SpaList resource_list;

View file

@ -30,14 +30,16 @@ extern "C" {
typedef struct _PinosModule PinosModule;
struct _PinosModule {
PinosCore *core;
SpaList link;
PinosCore *core;
SpaList link;
PinosGlobal *global;
char *name;
char *filename;
char *args;
void *user_data;
PINOS_SIGNAL (destroy_signal, (PinosListener *listener,
PinosModule *module));
};

View file

@ -461,10 +461,9 @@ init_complete (PinosNode *this)
pinos_log_debug ("node %p: init completed", this);
impl->async_init = false;
spa_list_insert (this->core->node_list.prev, &this->link);
pinos_node_update_state (this, PINOS_NODE_STATE_SUSPENDED, NULL);
spa_list_insert (this->core->node_list.prev, &this->link);
this->global = pinos_core_add_global (this->core,
NULL,
this->core->uri.node,
@ -512,6 +511,7 @@ pinos_node_new (PinosCore *core,
pinos_log_warn ("node %p: error setting callback", this);
pinos_signal_init (&this->destroy_signal);
pinos_signal_init (&this->free_signal);
pinos_signal_init (&this->async_complete);
pinos_signal_init (&this->transport_changed);
pinos_signal_init (&this->loop_changed);
@ -564,6 +564,7 @@ do_node_remove_done (SpaLoop *loop,
void *user_data)
{
PinosNode *this = user_data;
PinosNodeImpl *impl = SPA_CONTAINER_OF (this, PinosNodeImpl, this);
PinosPort *port, *tmp;
pinos_log_debug ("node %p: remove done, destroy ports", this);
@ -573,10 +574,21 @@ do_node_remove_done (SpaLoop *loop,
spa_list_for_each_safe (port, tmp, &this->output_ports, link)
pinos_port_destroy (port);
pinos_main_loop_defer_complete (this->core->main_loop,
this,
seq,
SPA_RESULT_OK);
pinos_log_debug ("node %p: free", this);
pinos_signal_emit (&this->free_signal, this);
if (this->transport)
pinos_transport_destroy (this->transport);
if (this->input_port_map)
free (this->input_port_map);
if (this->output_port_map)
free (this->output_port_map);
free (this->name);
free (this->error);
if (this->properties)
pinos_properties_free (this->properties);
free (impl);
return SPA_RESULT_OK;
}
@ -620,24 +632,6 @@ do_node_remove (SpaLoop *loop,
return res;
}
static void
sync_destroy (void *object,
void *data,
SpaResult res,
uint32_t id)
{
PinosNode *this = object;
PinosNodeImpl *impl = SPA_CONTAINER_OF (this, PinosNodeImpl, this);
pinos_log_debug ("node %p: sync destroy", this);
free (this->name);
free (this->error);
if (this->properties)
pinos_properties_free (this->properties);
free (impl);
}
/**
* pinos_node_destroy:
* @node: a #PinosNode
@ -648,7 +642,6 @@ sync_destroy (void *object,
void
pinos_node_destroy (PinosNode * this)
{
SpaResult res;
PinosNodeImpl *impl = SPA_CONTAINER_OF (this, PinosNodeImpl, this);
PinosResource *resource, *tmp;
@ -661,24 +654,12 @@ pinos_node_destroy (PinosNode * this)
spa_list_for_each_safe (resource, tmp, &this->resource_list, link)
pinos_resource_destroy (resource);
res = pinos_loop_invoke (this->data_loop->loop,
do_node_remove,
impl->seq++,
0,
NULL,
this);
pinos_main_loop_defer (this->core->main_loop,
this,
res,
NULL,
NULL);
pinos_main_loop_defer (this->core->main_loop,
this,
SPA_RESULT_WAIT_SYNC,
sync_destroy,
this);
pinos_loop_invoke (this->data_loop->loop,
do_node_remove,
impl->seq++,
0,
NULL,
this);
}
/**
@ -771,9 +752,6 @@ pinos_node_set_state (PinosNode *node,
res = suspend_node (node);
break;
case PINOS_NODE_STATE_INITIALIZING:
break;
case PINOS_NODE_STATE_IDLE:
res = pause_node (node);
break;

View file

@ -79,6 +79,8 @@ struct _PinosNode {
PINOS_SIGNAL (destroy_signal, (PinosListener *listener,
PinosNode *object));
PINOS_SIGNAL (free_signal, (PinosListener *listener,
PinosNode *object));
PINOS_SIGNAL (async_complete, (PinosListener *listener,
PinosNode *node,

View file

@ -56,17 +56,6 @@ pinos_port_new (PinosNode *node,
return this;
}
static void
sync_destroy (void *object,
void *data,
SpaResult res,
uint32_t id)
{
PinosPort *port = object;
free (port);
}
void
pinos_port_destroy (PinosPort *port)
{
@ -83,11 +72,7 @@ pinos_port_destroy (PinosPort *port)
port->buffers = NULL;
port->n_buffers = 0;
pinos_main_loop_defer (port->node->core->main_loop,
port,
SPA_RESULT_WAIT_SYNC,
sync_destroy,
port);
free (port);
}
static SpaResult

View file

@ -59,28 +59,12 @@ pinos_resource_new (PinosClient *client,
return this;
}
static void
sync_destroy (void *object,
void *data,
SpaResult res,
uint32_t id)
{
PinosResource *resource = object;
pinos_log_debug ("resource %p: sync destroy", resource);
free (resource);
}
SpaResult
void
pinos_resource_destroy (PinosResource *resource)
{
pinos_log_debug ("resource %p: destroy", resource);
pinos_signal_emit (&resource->destroy_signal, resource);
pinos_map_remove (&resource->client->objects, resource->id);
if (resource->destroy)
resource->destroy (resource);
if (resource->client->core_resource) {
PinosMessageRemoveId m;
m.id = resource->id;
@ -90,13 +74,13 @@ pinos_resource_destroy (PinosResource *resource)
true);
}
pinos_main_loop_defer (resource->core->main_loop,
resource,
SPA_RESULT_WAIT_SYNC,
sync_destroy,
resource);
pinos_map_remove (&resource->client->objects, resource->id);
return SPA_RESULT_OK;
if (resource->destroy)
resource->destroy (resource);
pinos_log_debug ("resource %p: free", resource);
free (resource);
}
void

View file

@ -62,7 +62,7 @@ PinosResource * pinos_resource_new (PinosClient *client,
uint32_t type,
void *object,
PinosDestroy destroy);
SpaResult pinos_resource_destroy (PinosResource *resource);
void pinos_resource_destroy (PinosResource *resource);
void pinos_resource_set_dispatch (PinosResource *resource,
PinosDispatchFunc func,