int snd_config_update_free(snd_config_update_t *update);
int snd_config_update_free_global(void);
+int snd_config_update_ref(snd_config_t **top);
+void snd_config_ref(snd_config_t *top);
+void snd_config_unref(snd_config_t *top);
+
int snd_config_search(snd_config_t *config, const char *key,
snd_config_t **result);
int snd_config_searchv(snd_config_t *config,
struct _snd_config {
char *id;
snd_config_type_t type;
+ int refcount; /* default = 0 */
union {
long integer;
long long integer64;
* If the node is a compound node, its descendants (the whole subtree)
* are deleted recursively.
*
+ * The function is supposed to be called only for locally copied config
+ * trees. For the global tree, take the reference via #snd_config_update_ref
+ * and free it via #snd_config_unref.
+ *
* \par Conforming to:
* LSB 3.2
*
int snd_config_delete(snd_config_t *config)
{
assert(config);
+ if (config->refcount > 0) {
+ config->refcount--;
+ return 0;
+ }
switch (config->type) {
case SND_CONFIG_TYPE_COMPOUND:
{
* \warning Whenever #snd_config is updated, all string pointers and
* configuration node handles previously obtained from it may become
* invalid.
+ * For safer operations, use #snd_config_update_ref and release the config
+ * via #snd_config_unref.
*
* \par Errors:
* Any errors encountered when parsing the input or returned by hooks or
return err;
}
+/**
+ * \brief Updates #snd_config and takes its reference.
+ * \return 0 if #snd_config was up to date, 1 if #snd_config was
+ * updated, otherwise a negative error code.
+ *
+ * Unlike #snd_config_update, this function increases a reference counter
+ * so that the obtained tree won't be deleted until unreferenced by
+ * #snd_config_unref.
+ *
+ * This function is supposed to be thread-safe.
+ */
+int snd_config_update_ref(snd_config_t **top)
+{
+ int err;
+
+ if (top)
+ *top = NULL;
+ snd_config_lock();
+ err = snd_config_update_r(&snd_config, &snd_config_global_update, NULL);
+ if (err >= 0) {
+ if (snd_config) {
+ if (top) {
+ snd_config->refcount++;
+ *top = snd_config;
+ }
+ } else {
+ err = -ENODEV;
+ }
+ }
+ snd_config_unlock();
+ return err;
+}
+
+/**
+ * \brief Take the reference of the config tree.
+ *
+ * Increases a reference counter of the given config tree.
+ *
+ * This function is supposed to be thread-safe.
+ */
+void snd_config_ref(snd_config_t *cfg)
+{
+ snd_config_lock();
+ if (cfg)
+ cfg->refcount++;
+ snd_config_unlock();
+}
+
+/**
+ * \brief Unreference the config tree.
+ *
+ * Decreases a reference counter of the given config tree, and eventually
+ * deletes the tree if all references are gone. This is the counterpart of
+ * #snd_config_unref.
+ *
+ * Also, the config taken via #snd_config_update_ref must be unreferenced
+ * by this function, too.
+ *
+ * This function is supposed to be thread-safe.
+ */
+void snd_config_unref(snd_config_t *cfg)
+{
+ snd_config_lock();
+ if (cfg)
+ snd_config_delete(cfg);
+ snd_config_unlock();
+}
+
/**
* \brief Frees a private update structure.
* \param[in] update The private update structure to free.
*/
int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode)
{
+ snd_config_t *top;
int err;
+
assert(ctlp && name);
- err = snd_config_update();
+ err = snd_config_update_ref(&top);
if (err < 0)
return err;
- return snd_ctl_open_noupdate(ctlp, snd_config, name, mode);
+ err = snd_ctl_open_noupdate(ctlp, top, name, mode);
+ snd_config_unref(top);
+ return err;
}
/**
*/
int snd_hwdep_open(snd_hwdep_t **hwdep, const char *name, int mode)
{
+ snd_config_t *top;
int err;
+
assert(hwdep && name);
- err = snd_config_update();
+ err = snd_config_update_ref(&top);
if (err < 0)
return err;
- return snd_hwdep_open_noupdate(hwdep, snd_config, name, mode);
+ err = snd_hwdep_open_noupdate(hwdep, top, name, mode);
+ snd_config_unref(top);
+ return err;
}
/**
int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_stream_t stream, int mode)
{
+ snd_config_t *top;
int err;
+
assert(pcmp && name);
- err = snd_config_update();
+ err = snd_config_update_ref(&top);
if (err < 0)
return err;
- return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0);
+ err = snd_pcm_open_noupdate(pcmp, top, name, stream, mode, 0);
+ snd_config_unref(top);
+ return err;
}
/**
int snd_rawmidi_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
const char *name, int mode)
{
+ snd_config_t *top;
int err;
+
assert((inputp || outputp) && name);
- err = snd_config_update();
+ err = snd_config_update_ref(&top);
if (err < 0)
return err;
- return snd_rawmidi_open_noupdate(inputp, outputp, snd_config, name, mode);
+ err = snd_rawmidi_open_noupdate(inputp, outputp, top, name, mode);
+ snd_config_unref(top);
+ return err;
}
/**
int snd_seq_open(snd_seq_t **seqp, const char *name,
int streams, int mode)
{
+ snd_config_t *top;
int err;
+
assert(seqp && name);
- err = snd_config_update();
+ err = snd_config_update_ref(&top);
if (err < 0)
return err;
- return snd_seq_open_noupdate(seqp, snd_config, name, streams, mode, 0);
+ err = snd_seq_open_noupdate(seqp, top, name, streams, mode, 0);
+ snd_config_unref(top);
+ return err;
}
/**
*/
int snd_timer_open(snd_timer_t **timer, const char *name, int mode)
{
+ snd_config_t *top;
int err;
+
assert(timer && name);
- err = snd_config_update();
+ err = snd_config_update_ref(&top);
if (err < 0)
return err;
- return snd_timer_open_noupdate(timer, snd_config, name, mode);
+ err = snd_timer_open_noupdate(timer, top, name, mode);
+ snd_config_unref(top);
+ return err;
}
/**
*/
int snd_timer_query_open(snd_timer_query_t **timer, const char *name, int mode)
{
+ snd_config_t *top;
int err;
+
assert(timer && name);
- err = snd_config_update();
+ err = snd_config_update_ref(&top);
if (err < 0)
return err;
- return snd_timer_query_open_noupdate(timer, snd_config, name, mode);
+ err = snd_timer_query_open_noupdate(timer, top, name, mode);
+ snd_config_unref(top);
+ return err;
}
/**