|
|
|
/* GSL Engine - Flow module operation engine
|
|
|
|
* Copyright (C) 2001 Tim Janik
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser 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
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General
|
|
|
|
* Public License along with this library; if not, write to the
|
|
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
#include "gsloputil.h"
|
|
|
|
|
|
|
|
#include "gslcommon.h"
|
|
|
|
#include "gslopnode.h"
|
|
|
|
#include "gslopschedule.h"
|
|
|
|
#include "gslsignal.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
|
|
/* --- UserThread --- */
|
|
|
|
GslOStream*
|
|
|
|
_engine_alloc_ostreams (guint n)
|
|
|
|
{
|
|
|
|
if (n)
|
|
|
|
{
|
|
|
|
guint i = sizeof (GslOStream) * n + sizeof (gfloat) * gsl_engine_block_size () * n;
|
|
|
|
GslOStream *streams = gsl_alloc_memblock0 (i);
|
|
|
|
gfloat *buffers = (gfloat*) (streams + n);
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
streams[i].values = buffers;
|
|
|
|
buffers += gsl_engine_block_size ();
|
|
|
|
}
|
|
|
|
return streams;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_node (EngineNode *node)
|
|
|
|
{
|
|
|
|
guint j;
|
|
|
|
|
|
|
|
g_return_if_fail (node != NULL);
|
|
|
|
g_return_if_fail (node->output_nodes == NULL);
|
|
|
|
g_return_if_fail (node->integrated == FALSE);
|
|
|
|
g_return_if_fail (node->sched_tag == FALSE);
|
|
|
|
g_return_if_fail (node->sched_router_tag == FALSE);
|
|
|
|
|
|
|
|
if (node->module.klass->free)
|
|
|
|
node->module.klass->free (node->module.user_data, node->module.klass);
|
|
|
|
gsl_rec_mutex_destroy (&node->rec_mutex);
|
|
|
|
if (node->module.ostreams)
|
|
|
|
{
|
|
|
|
guint n = ENGINE_NODE_N_OSTREAMS (node);
|
|
|
|
guint i = sizeof (GslOStream) * n + sizeof (gfloat) * gsl_engine_block_size () * n;
|
|
|
|
|
|
|
|
gsl_free_memblock (i, node->module.ostreams);
|
|
|
|
gsl_delete_structs (EngineOutput, ENGINE_NODE_N_OSTREAMS (node), node->outputs);
|
|
|
|
}
|
|
|
|
if (node->module.istreams)
|
|
|
|
{
|
|
|
|
gsl_delete_structs (GslIStream, ENGINE_NODE_N_ISTREAMS (node), node->module.istreams);
|
|
|
|
gsl_delete_structs (EngineInput, ENGINE_NODE_N_ISTREAMS (node), node->inputs);
|
|
|
|
}
|
|
|
|
for (j = 0; j < ENGINE_NODE_N_JSTREAMS (node); j++)
|
|
|
|
{
|
|
|
|
g_free (node->jinputs[j]);
|
|
|
|
g_free (node->module.jstreams[j].values);
|
|
|
|
}
|
|
|
|
if (node->module.jstreams)
|
|
|
|
{
|
|
|
|
gsl_delete_structs (GslJStream, ENGINE_NODE_N_JSTREAMS (node), node->module.jstreams);
|
|
|
|
gsl_delete_structs (EngineJInput*, ENGINE_NODE_N_JSTREAMS (node), node->jinputs);
|
|
|
|
}
|
|
|
|
gsl_delete_struct (EngineNode, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_job (GslJob *job)
|
|
|
|
{
|
|
|
|
g_return_if_fail (job != NULL);
|
|
|
|
|
|
|
|
switch (job->job_id)
|
|
|
|
{
|
|
|
|
case ENGINE_JOB_ACCESS:
|
|
|
|
if (job->data.access.free_func)
|
|
|
|
job->data.access.free_func (job->data.access.data);
|
|
|
|
break;
|
|
|
|
case ENGINE_JOB_DEBUG:
|
|
|
|
g_free (job->data.debug);
|
|
|
|
break;
|
|
|
|
case ENGINE_JOB_ADD_POLL:
|
|
|
|
case ENGINE_JOB_REMOVE_POLL:
|
|
|
|
g_free (job->data.poll.fds);
|
|
|
|
if (job->data.poll.free_func)
|
|
|
|
job->data.poll.free_func (job->data.poll.data);
|
|
|
|
break;
|
|
|
|
case ENGINE_JOB_DISCARD:
|
|
|
|
free_node (job->data.node);
|
|
|
|
break;
|
|
|
|
default: ;
|
|
|
|
}
|
|
|
|
gsl_delete_struct (GslJob, job);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_flow_job (EngineFlowJob *fjob)
|
|
|
|
{
|
|
|
|
switch (fjob->fjob_id)
|
|
|
|
{
|
|
|
|
case ENGINE_FLOW_JOB_SUSPEND:
|
|
|
|
case ENGINE_FLOW_JOB_RESUME:
|
|
|
|
gsl_delete_struct (EngineFlowJobAny, &fjob->any);
|
|
|
|
break;
|
|
|
|
case ENGINE_FLOW_JOB_ACCESS:
|
|
|
|
if (fjob->access.free_func)
|
|
|
|
fjob->access.free_func (fjob->access.data);
|
|
|
|
gsl_delete_struct (EngineFlowJobAccess, &fjob->access);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_assert_not_reached ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_free_trans (GslTrans *trans)
|
|
|
|
{
|
|
|
|
GslJob *job;
|
|
|
|
|
|
|
|
g_return_if_fail (trans != NULL);
|
|
|
|
g_return_if_fail (trans->comitted == FALSE);
|
|
|
|
if (trans->jobs_tail)
|
|
|
|
g_return_if_fail (trans->jobs_tail->next == NULL); /* paranoid */
|
|
|
|
|
|
|
|
job = trans->jobs_head;
|
|
|
|
while (job)
|
|
|
|
{
|
|
|
|
GslJob *tmp = job->next;
|
|
|
|
|
|
|
|
free_job (job);
|
|
|
|
job = tmp;
|
|
|
|
}
|
|
|
|
gsl_delete_struct (GslTrans, trans);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* -- master node list --- */
|
|
|
|
static EngineNode *master_node_list_head = NULL;
|
|
|
|
static EngineNode *master_node_list_tail = NULL;
|
|
|
|
|
|
|
|
EngineNode*
|
|
|
|
_engine_mnl_head (void)
|
|
|
|
{
|
|
|
|
return master_node_list_head;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_mnl_remove (EngineNode *node)
|
|
|
|
{
|
|
|
|
g_return_if_fail (node->integrated == TRUE);
|
|
|
|
|
|
|
|
node->integrated = FALSE;
|
|
|
|
/* remove */
|
|
|
|
if (node->mnl_prev)
|
|
|
|
node->mnl_prev->mnl_next = node->mnl_next;
|
|
|
|
else
|
|
|
|
master_node_list_head = node->mnl_next;
|
|
|
|
if (node->mnl_next)
|
|
|
|
node->mnl_next->mnl_prev = node->mnl_prev;
|
|
|
|
else
|
|
|
|
master_node_list_tail = node->mnl_prev;
|
|
|
|
node->mnl_prev = NULL;
|
|
|
|
node->mnl_next = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_mnl_integrate (EngineNode *node)
|
|
|
|
{
|
|
|
|
g_return_if_fail (node->integrated == FALSE);
|
|
|
|
g_return_if_fail (node->flow_jobs == NULL);
|
|
|
|
|
|
|
|
node->integrated = TRUE;
|
|
|
|
/* append */
|
|
|
|
if (master_node_list_tail)
|
|
|
|
master_node_list_tail->mnl_next = node;
|
|
|
|
node->mnl_prev = master_node_list_tail;
|
|
|
|
master_node_list_tail = node;
|
|
|
|
if (!master_node_list_head)
|
|
|
|
master_node_list_head = master_node_list_tail;
|
|
|
|
g_assert (node->mnl_next == NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_mnl_reorder (EngineNode *node)
|
|
|
|
{
|
|
|
|
EngineNode *sibling;
|
|
|
|
|
|
|
|
g_return_if_fail (node->integrated == TRUE);
|
|
|
|
|
|
|
|
/* the master node list is partially sorted. that is, all
|
|
|
|
* nodes which are not scheduled and have pending flow_jobs
|
|
|
|
* are agglomerated at the head.
|
|
|
|
*/
|
|
|
|
sibling = node->mnl_prev ? node->mnl_prev : node->mnl_next;
|
|
|
|
if (sibling && GSL_MNL_HEAD_NODE (node) != GSL_MNL_HEAD_NODE (sibling))
|
|
|
|
{
|
|
|
|
/* remove */
|
|
|
|
if (node->mnl_prev)
|
|
|
|
node->mnl_prev->mnl_next = node->mnl_next;
|
|
|
|
else
|
|
|
|
master_node_list_head = node->mnl_next;
|
|
|
|
if (node->mnl_next)
|
|
|
|
node->mnl_next->mnl_prev = node->mnl_prev;
|
|
|
|
else
|
|
|
|
master_node_list_tail = node->mnl_prev;
|
|
|
|
if (GSL_MNL_HEAD_NODE (node)) /* move towards head */
|
|
|
|
{
|
|
|
|
/* prepend to non-NULL list */
|
|
|
|
master_node_list_head->mnl_prev = node;
|
|
|
|
node->mnl_next = master_node_list_head;
|
|
|
|
master_node_list_head = node;
|
|
|
|
node->mnl_prev = NULL;
|
|
|
|
}
|
|
|
|
else /* move towards tail */
|
|
|
|
{
|
|
|
|
/* append to non-NULL list */
|
|
|
|
master_node_list_tail->mnl_next = node;
|
|
|
|
node->mnl_prev = master_node_list_tail;
|
|
|
|
master_node_list_tail = node;
|
|
|
|
node->mnl_next = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* --- const value blocks --- */
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
guint n_nodes;
|
|
|
|
gfloat **nodes;
|
|
|
|
guint8 *nodes_used;
|
|
|
|
} ConstValuesArray;
|
|
|
|
|
|
|
|
static const guint8 CONST_VALUES_EXPIRE = 16; /* expire value after being unused for 16 times */
|
|
|
|
|
|
|
|
static inline gfloat**
|
|
|
|
const_values_lookup_nextmost (ConstValuesArray *array,
|
|
|
|
gfloat key_value)
|
|
|
|
{
|
|
|
|
guint n_nodes = array->n_nodes;
|
|
|
|
|
|
|
|
if (n_nodes > 0)
|
|
|
|
{
|
|
|
|
gfloat **nodes = array->nodes;
|
|
|
|
gfloat **check;
|
|
|
|
|
|
|
|
nodes -= 1;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
gfloat cmp;
|
|
|
|
|
|
|
|
i = (n_nodes + 1) >> 1;
|
|
|
|
check = nodes + i;
|
|
|
|
cmp = key_value - **check;
|
|
|
|
if (cmp > GSL_SIGNAL_EPSILON)
|
|
|
|
{
|
|
|
|
n_nodes -= i;
|
|
|
|
nodes = check;
|
|
|
|
}
|
|
|
|
else if (cmp < -GSL_SIGNAL_EPSILON)
|
|
|
|
n_nodes = i - 1;
|
|
|
|
else /* cmp ~==~ 0.0 */
|
|
|
|
return check; /* matched */
|
|
|
|
}
|
|
|
|
while (n_nodes);
|
|
|
|
|
|
|
|
return check; /* nextmost */
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline guint
|
|
|
|
upper_power2 (guint number)
|
|
|
|
{
|
|
|
|
return gsl_alloc_upper_power2 (MAX (number, 8));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
const_values_insert (ConstValuesArray *array,
|
|
|
|
guint index,
|
|
|
|
gfloat *value_block)
|
|
|
|
{
|
|
|
|
if (array->n_nodes == 0)
|
|
|
|
{
|
|
|
|
guint new_size = upper_power2 (sizeof (gfloat*));
|
|
|
|
|
|
|
|
array->nodes = g_realloc (array->nodes, new_size);
|
|
|
|
array->nodes_used = g_realloc (array->nodes_used, new_size / sizeof (gfloat*));
|
|
|
|
array->n_nodes = 1;
|
|
|
|
|
|
|
|
g_assert (index == 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
guint n_nodes = array->n_nodes++;
|
|
|
|
|
|
|
|
if (*array->nodes[index] < *value_block)
|
|
|
|
index++;
|
|
|
|
|
|
|
|
if (1)
|
|
|
|
{
|
|
|
|
guint new_size = upper_power2 (array->n_nodes * sizeof (gfloat*));
|
|
|
|
guint old_size = upper_power2 (n_nodes * sizeof (gfloat*));
|
|
|
|
|
|
|
|
if (new_size != old_size)
|
|
|
|
{
|
|
|
|
array->nodes = g_realloc (array->nodes, new_size);
|
|
|
|
array->nodes_used = g_realloc (array->nodes_used, new_size / sizeof(gfloat*));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_memmove (array->nodes + index + 1, array->nodes + index, (n_nodes - index) * sizeof (array->nodes[0]));
|
|
|
|
g_memmove (array->nodes_used + index + 1, array->nodes_used + index, (n_nodes - index) * sizeof (array->nodes_used[0]));
|
|
|
|
}
|
|
|
|
|
|
|
|
array->nodes[index] = value_block;
|
|
|
|
array->nodes_used[index] = CONST_VALUES_EXPIRE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ConstValuesArray cvalue_array = { 0, NULL, NULL };
|
|
|
|
|
|
|
|
gfloat*
|
|
|
|
gsl_engine_const_values (gfloat value)
|
|
|
|
{
|
|
|
|
extern const gfloat gsl_engine_master_zero_block[];
|
|
|
|
gfloat **block;
|
|
|
|
|
|
|
|
if (fabs (value) < GSL_SIGNAL_EPSILON)
|
|
|
|
return (gfloat*) gsl_engine_master_zero_block;
|
|
|
|
|
|
|
|
block = const_values_lookup_nextmost (&cvalue_array, value);
|
|
|
|
|
|
|
|
/* found correct match? */
|
|
|
|
if (block && fabs (**block - value) < GSL_SIGNAL_EPSILON)
|
|
|
|
{
|
|
|
|
cvalue_array.nodes_used[block - cvalue_array.nodes] = CONST_VALUES_EXPIRE;
|
|
|
|
return *block;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* create new value block */
|
|
|
|
gfloat *values = g_new (gfloat, gsl_engine_block_size ());
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
for (i = 0; i < gsl_engine_block_size (); i++)
|
|
|
|
values[i] = value;
|
|
|
|
|
|
|
|
if (block)
|
|
|
|
const_values_insert (&cvalue_array, block - cvalue_array.nodes, values);
|
|
|
|
else
|
|
|
|
const_values_insert (&cvalue_array, 0, values);
|
|
|
|
|
|
|
|
return values;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_recycle_const_values (void)
|
|
|
|
{
|
|
|
|
gfloat **nodes = cvalue_array.nodes;
|
|
|
|
guint8 *used = cvalue_array.nodes_used;
|
|
|
|
guint count = cvalue_array.n_nodes, e = 0, i;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
used[i]--; /* invariant: use counts are never 0 */
|
|
|
|
|
|
|
|
if (used[i] == 0)
|
|
|
|
g_free (nodes[i]);
|
|
|
|
else /* preserve node */
|
|
|
|
{
|
|
|
|
if (e < i)
|
|
|
|
{
|
|
|
|
nodes[e] = nodes[i];
|
|
|
|
used[e] = used[i];
|
|
|
|
}
|
|
|
|
e++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cvalue_array.n_nodes = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --- job transactions --- */
|
|
|
|
static GslMutex cqueue_trans = { 0, };
|
|
|
|
static GslTrans *cqueue_trans_pending_head = NULL;
|
|
|
|
static GslTrans *cqueue_trans_pending_tail = NULL;
|
|
|
|
static GslCond cqueue_trans_cond = { 0, };
|
|
|
|
static GslTrans *cqueue_trans_trash = NULL;
|
|
|
|
static GslTrans *cqueue_trans_active_head = NULL;
|
|
|
|
static GslTrans *cqueue_trans_active_tail = NULL;
|
|
|
|
static EngineFlowJob *cqueue_trash_fjobs = NULL;
|
|
|
|
static GslJob *cqueue_trans_job = NULL;
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_enqueue_trans (GslTrans *trans)
|
|
|
|
{
|
|
|
|
g_return_if_fail (trans != NULL);
|
|
|
|
g_return_if_fail (trans->comitted == TRUE);
|
|
|
|
g_return_if_fail (trans->jobs_head != NULL);
|
|
|
|
g_return_if_fail (trans->cqt_next == NULL);
|
|
|
|
|
|
|
|
GSL_SPIN_LOCK (&cqueue_trans);
|
|
|
|
if (cqueue_trans_pending_tail)
|
|
|
|
{
|
|
|
|
cqueue_trans_pending_tail->cqt_next = trans;
|
|
|
|
cqueue_trans_pending_tail->jobs_tail->next = trans->jobs_head;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cqueue_trans_pending_head = trans;
|
|
|
|
cqueue_trans_pending_tail = trans;
|
|
|
|
GSL_SPIN_UNLOCK (&cqueue_trans);
|
|
|
|
gsl_cond_signal (&cqueue_trans_cond);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_wait_on_trans (void)
|
|
|
|
{
|
|
|
|
GSL_SPIN_LOCK (&cqueue_trans);
|
|
|
|
while (cqueue_trans_pending_head || cqueue_trans_active_head)
|
|
|
|
gsl_cond_wait (&cqueue_trans_cond, &cqueue_trans);
|
|
|
|
GSL_SPIN_UNLOCK (&cqueue_trans);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
_engine_job_pending (void)
|
|
|
|
{
|
|
|
|
gboolean pending = cqueue_trans_job != NULL;
|
|
|
|
|
|
|
|
if (!pending)
|
|
|
|
{
|
|
|
|
GSL_SPIN_LOCK (&cqueue_trans);
|
|
|
|
pending = cqueue_trans_pending_head != NULL;
|
|
|
|
GSL_SPIN_UNLOCK (&cqueue_trans);
|
|
|
|
}
|
|
|
|
return pending;
|
|
|
|
}
|
|
|
|
|
|
|
|
GslJob*
|
|
|
|
_engine_pop_job (void) /* (glong max_useconds) */
|
|
|
|
{
|
|
|
|
/* clean up if necessary and try fetching new jobs */
|
|
|
|
if (!cqueue_trans_job)
|
|
|
|
{
|
|
|
|
if (cqueue_trans_active_head)
|
|
|
|
{
|
|
|
|
GSL_SPIN_LOCK (&cqueue_trans);
|
|
|
|
/* get rid of processed transaction and
|
|
|
|
* signal UserThread which might be in
|
|
|
|
* op_com_wait_on_trans()
|
|
|
|
*/
|
|
|
|
cqueue_trans_active_tail->cqt_next = cqueue_trans_trash;
|
|
|
|
cqueue_trans_trash = cqueue_trans_active_head;
|
|
|
|
/* fetch new transaction */
|
|
|
|
cqueue_trans_active_head = cqueue_trans_pending_head;
|
|
|
|
cqueue_trans_active_tail = cqueue_trans_pending_tail;
|
|
|
|
cqueue_trans_pending_head = NULL;
|
|
|
|
cqueue_trans_pending_tail = NULL;
|
|
|
|
GSL_SPIN_UNLOCK (&cqueue_trans);
|
|
|
|
gsl_cond_signal (&cqueue_trans_cond);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GSL_SPIN_LOCK (&cqueue_trans);
|
|
|
|
/* fetch new transaction */
|
|
|
|
cqueue_trans_active_head = cqueue_trans_pending_head;
|
|
|
|
cqueue_trans_active_tail = cqueue_trans_pending_tail;
|
|
|
|
cqueue_trans_pending_head = NULL;
|
|
|
|
cqueue_trans_pending_tail = NULL;
|
|
|
|
GSL_SPIN_UNLOCK (&cqueue_trans);
|
|
|
|
}
|
|
|
|
cqueue_trans_job = cqueue_trans_active_head ? cqueue_trans_active_head->jobs_head : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pick new job and out of here */
|
|
|
|
if (cqueue_trans_job)
|
|
|
|
{
|
|
|
|
GslJob *job = cqueue_trans_job;
|
|
|
|
|
|
|
|
cqueue_trans_job = job->next;
|
|
|
|
return job;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* wait until jobs are present */
|
|
|
|
if (max_useconds != 0)
|
|
|
|
{
|
|
|
|
GSL_SPIN_LOCK (&cqueue_trans);
|
|
|
|
if (!cqueue_trans_pending_head)
|
|
|
|
gsl_cond_wait_timed (&cqueue_trans_cond,
|
|
|
|
&cqueue_trans,
|
|
|
|
max_useconds);
|
|
|
|
GSL_SPIN_UNLOCK (&cqueue_trans);
|
|
|
|
|
|
|
|
/* there may be jobs now, start from scratch */
|
|
|
|
return op_com_pop_job_timed (max_useconds < 0 ? -1 : 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* time expired, no jobs... */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* --- user thread garbage collection --- */
|
|
|
|
/**
|
|
|
|
* gsl_engine_garbage_collect
|
|
|
|
*
|
|
|
|
* GSL Engine user thread function. Collects processed jobs
|
|
|
|
* and transactions from the engine and frees them, this
|
|
|
|
* involves callback invocation of GslFreeFunc() functions,
|
|
|
|
* e.g. from gsl_job_access() or gsl_flow_job_access()
|
|
|
|
* jobs.
|
|
|
|
* This function may only be called from the user thread,
|
|
|
|
* as GslFreeFunc() functions are guranteed to be executed
|
|
|
|
* in the user thread.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
gsl_engine_garbage_collect (void)
|
|
|
|
{
|
|
|
|
GslTrans *trans;
|
|
|
|
EngineFlowJob *fjobs;
|
|
|
|
|
|
|
|
GSL_SPIN_LOCK (&cqueue_trans);
|
|
|
|
trans = cqueue_trans_trash;
|
|
|
|
cqueue_trans_trash = NULL;
|
|
|
|
fjobs = cqueue_trash_fjobs;
|
|
|
|
cqueue_trash_fjobs = NULL;
|
|
|
|
GSL_SPIN_UNLOCK (&cqueue_trans);
|
|
|
|
|
|
|
|
while (trans)
|
|
|
|
{
|
|
|
|
GslTrans *t = trans;
|
|
|
|
|
|
|
|
trans = t->cqt_next;
|
|
|
|
t->cqt_next = NULL;
|
|
|
|
t->jobs_tail->next = NULL;
|
|
|
|
t->comitted = FALSE;
|
|
|
|
_engine_free_trans (t);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fjobs)
|
|
|
|
{
|
|
|
|
EngineFlowJob *j = fjobs;
|
|
|
|
|
|
|
|
fjobs = j->any.next;
|
|
|
|
j->any.next = NULL;
|
|
|
|
free_flow_job (j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* --- node processing queue --- */
|
|
|
|
static GslMutex pqueue_mutex = { 0, };
|
|
|
|
static EngineSchedule *pqueue_schedule = NULL;
|
|
|
|
static guint pqueue_n_nodes = 0;
|
|
|
|
static guint pqueue_n_cycles = 0;
|
|
|
|
static GslCond pqueue_done_cond = { 0, };
|
|
|
|
static EngineFlowJob *pqueue_trash_fjobs_first = NULL;
|
|
|
|
static EngineFlowJob *pqueue_trash_fjobs_last = NULL;
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_set_schedule (EngineSchedule *sched)
|
|
|
|
{
|
|
|
|
g_return_if_fail (sched != NULL);
|
|
|
|
g_return_if_fail (sched->secured == TRUE);
|
|
|
|
|
|
|
|
GSL_SPIN_LOCK (&pqueue_mutex);
|
|
|
|
if_reject (pqueue_schedule)
|
|
|
|
{
|
|
|
|
GSL_SPIN_UNLOCK (&pqueue_mutex);
|
|
|
|
g_warning (G_STRLOC ": schedule already set");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pqueue_schedule = sched;
|
|
|
|
sched->in_pqueue = TRUE;
|
|
|
|
GSL_SPIN_UNLOCK (&pqueue_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_unset_schedule (EngineSchedule *sched)
|
|
|
|
{
|
|
|
|
EngineFlowJob *trash_fjobs_first, *trash_fjobs_last;
|
|
|
|
|
|
|
|
g_return_if_fail (sched != NULL);
|
|
|
|
|
|
|
|
GSL_SPIN_LOCK (&pqueue_mutex);
|
|
|
|
if_reject (pqueue_schedule != sched)
|
|
|
|
{
|
|
|
|
GSL_SPIN_UNLOCK (&pqueue_mutex);
|
|
|
|
g_warning (G_STRLOC ": schedule(%p) not currently set", sched);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if_reject (pqueue_n_nodes || pqueue_n_cycles)
|
|
|
|
g_warning (G_STRLOC ": schedule(%p) still busy", sched);
|
|
|
|
|
|
|
|
sched->in_pqueue = FALSE;
|
|
|
|
pqueue_schedule = NULL;
|
|
|
|
trash_fjobs_first = pqueue_trash_fjobs_first;
|
|
|
|
trash_fjobs_last = pqueue_trash_fjobs_last;
|
|
|
|
pqueue_trash_fjobs_first = NULL;
|
|
|
|
pqueue_trash_fjobs_last = NULL;
|
|
|
|
GSL_SPIN_UNLOCK (&pqueue_mutex);
|
|
|
|
|
|
|
|
if (trash_fjobs_first) /* move trash flow jobs */
|
|
|
|
{
|
|
|
|
GSL_SPIN_LOCK (&cqueue_trans);
|
|
|
|
trash_fjobs_last->any.next = cqueue_trash_fjobs;
|
|
|
|
cqueue_trash_fjobs = trash_fjobs_first;
|
|
|
|
GSL_SPIN_UNLOCK (&cqueue_trans);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EngineNode*
|
|
|
|
_engine_pop_unprocessed_node (void)
|
|
|
|
{
|
|
|
|
EngineNode *node;
|
|
|
|
|
|
|
|
GSL_SPIN_LOCK (&pqueue_mutex);
|
|
|
|
node = pqueue_schedule ? _engine_schedule_pop_node (pqueue_schedule) : NULL;
|
|
|
|
if (node)
|
|
|
|
pqueue_n_nodes += 1;
|
|
|
|
GSL_SPIN_UNLOCK (&pqueue_mutex);
|
|
|
|
|
|
|
|
if (node)
|
|
|
|
ENGINE_NODE_LOCK (node);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_push_processed_node (EngineNode *node)
|
|
|
|
{
|
|
|
|
g_return_if_fail (node != NULL);
|
|
|
|
g_return_if_fail (pqueue_n_nodes > 0);
|
|
|
|
g_return_if_fail (ENGINE_NODE_IS_SCHEDULED (node));
|
|
|
|
|
|
|
|
GSL_SPIN_LOCK (&pqueue_mutex);
|
|
|
|
g_assert (pqueue_n_nodes > 0); /* paranoid */
|
|
|
|
if (node->fjob_first) /* collect trash flow jobs */
|
|
|
|
{
|
|
|
|
node->fjob_last->any.next = pqueue_trash_fjobs_first;
|
|
|
|
pqueue_trash_fjobs_first = node->fjob_first;
|
|
|
|
if (!pqueue_trash_fjobs_last)
|
|
|
|
pqueue_trash_fjobs_last = node->fjob_last;
|
|
|
|
node->fjob_first = NULL;
|
|
|
|
node->fjob_last = NULL;
|
|
|
|
}
|
|
|
|
pqueue_n_nodes -= 1;
|
|
|
|
ENGINE_NODE_UNLOCK (node);
|
|
|
|
if (!pqueue_n_nodes && !pqueue_n_cycles && GSL_SCHEDULE_NONPOPABLE (pqueue_schedule))
|
|
|
|
gsl_cond_signal (&pqueue_done_cond);
|
|
|
|
GSL_SPIN_UNLOCK (&pqueue_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
GslRing*
|
|
|
|
_engine_pop_unprocessed_cycle (void)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_push_processed_cycle (GslRing *cycle)
|
|
|
|
{
|
|
|
|
g_return_if_fail (cycle != NULL);
|
|
|
|
g_return_if_fail (pqueue_n_cycles > 0);
|
|
|
|
g_return_if_fail (ENGINE_NODE_IS_SCHEDULED (cycle->data));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_engine_wait_on_unprocessed (void)
|
|
|
|
{
|
|
|
|
GSL_SPIN_LOCK (&pqueue_mutex);
|
|
|
|
while (pqueue_n_nodes || pqueue_n_cycles || !GSL_SCHEDULE_NONPOPABLE (pqueue_schedule))
|
|
|
|
gsl_cond_wait (&pqueue_done_cond, &pqueue_mutex);
|
|
|
|
GSL_SPIN_UNLOCK (&pqueue_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* --- initialization --- */
|
|
|
|
void
|
|
|
|
_gsl_init_engine_utils (void)
|
|
|
|
{
|
|
|
|
static gboolean initialized = FALSE;
|
|
|
|
|
|
|
|
g_assert (initialized == FALSE); /* single invocation */
|
|
|
|
initialized++;
|
|
|
|
|
|
|
|
gsl_mutex_init (&cqueue_trans);
|
|
|
|
gsl_cond_init (&cqueue_trans_cond);
|
|
|
|
gsl_mutex_init (&pqueue_mutex);
|
|
|
|
gsl_cond_init (&pqueue_done_cond);
|
|
|
|
}
|