+// LV2 Worker
+
+PluginLV2Work::PluginLV2Work()
+{
+ next = 0;
+ alloc = used = 0;
+ data = 0;
+}
+
+PluginLV2Work::~PluginLV2Work()
+{
+ delete [] data;
+}
+
+void PluginLV2Work::load(const void *vp, unsigned size)
+{
+ if( alloc < size ) {
+ delete [] data;
+ data = new char[alloc=size];
+ }
+ memcpy(data, vp, used=size);
+}
+
+PluginLV2Work *PluginLV2::get_work()
+{
+// must hold worker_lock
+ if( !work_avail ) return new PluginLV2Work();
+ PluginLV2Work *wp = work_avail;
+ work_avail = wp->next; wp->next = 0;
+ return wp;
+}
+
+void *PluginLV2::worker_func()
+{
+ pthread_mutex_lock(&worker_lock);
+ for(;;) {
+ while( !worker_done && !work_input )
+ pthread_cond_wait(&worker_ready, &worker_lock);
+ if( worker_done ) break;
+ PluginLV2Work *wp = work_input; work_input = wp->next;
+
+ pthread_mutex_unlock(&worker_lock);
+ worker_iface->work(inst, lv2_worker_respond, this, wp->used, wp->data);
+ pthread_mutex_lock(&worker_lock);
+ wp->next = work_avail; work_avail = wp;
+ }
+ pthread_mutex_unlock(&worker_lock);
+ return NULL;
+}
+void *PluginLV2::worker_func(void* vp)
+{
+ PluginLV2 *the = (PluginLV2 *)vp;
+ return the->worker_func();
+}
+
+void PluginLV2::worker_start()
+{
+ pthread_create(&worker_thread, 0, worker_func, this);
+}
+
+void PluginLV2::worker_stop()
+{
+ if( !worker_done ) {
+ worker_done = 1;
+ pthread_mutex_lock(&worker_lock);
+ pthread_cond_signal(&worker_ready);
+ pthread_mutex_unlock(&worker_lock);
+ pthread_join(worker_thread, 0);
+ worker_thread = 0;
+ }
+ work_stop(work_avail);
+ work_stop(work_input);
+ work_stop(work_output);
+ work_tail = &work_output;
+}
+
+void PluginLV2::work_stop(PluginLV2Work *&work)
+{
+ while( work ) {
+ PluginLV2Work *wp = work;
+ work = wp->next;
+ delete wp;
+ }
+}
+
+LV2_Worker_Status PluginLV2::worker_schedule(uint32_t inp_size, const void *inp_data)
+{
+ if( is_forked() ) {
+ if( !pthread_mutex_trylock(&worker_lock) ) {
+ PluginLV2Work *wp = get_work();
+ wp->load(inp_data, inp_size);
+ wp->next = work_input; work_input = wp;
+ pthread_cond_signal(&worker_ready);
+ pthread_mutex_unlock(&worker_lock);
+ }
+ }
+ else if( worker_iface )
+ worker_iface->work(inst, lv2_worker_respond, this, inp_size, inp_data);
+ return LV2_WORKER_SUCCESS;
+}
+LV2_Worker_Status PluginLV2::lv2_worker_schedule(LV2_Worker_Schedule_Handle vp,
+ uint32_t inp_size, const void *inp_data)
+{
+ PluginLV2 *the = (PluginLV2 *)vp;
+ return the->worker_schedule(inp_size, inp_data);
+}
+
+LV2_Worker_Status PluginLV2::worker_respond(uint32_t out_size, const void *out_data)
+{
+ pthread_mutex_lock(&worker_lock);
+ PluginLV2Work *wp = get_work();
+ wp->load(out_data, out_size);
+ *work_tail = wp; work_tail = &wp->next;
+ pthread_mutex_unlock(&worker_lock);
+ return LV2_WORKER_SUCCESS;
+}
+LV2_Worker_Status PluginLV2::lv2_worker_respond(LV2_Worker_Respond_Handle vp,
+ uint32_t out_size, const void *out_data)
+{
+ PluginLV2 *the = (PluginLV2 *)vp;
+ return the->worker_respond(out_size, out_data);
+}
+
+void PluginLV2::worker_responses()
+{
+ pthread_mutex_lock(&worker_lock);
+ while( work_output ) {
+ PluginLV2Work *rp = work_output;
+ if( !(work_output=rp->next) ) work_tail = &work_output;
+ pthread_mutex_unlock(&worker_lock);
+ worker_iface->work_response(inst, rp->used, rp->data);
+ pthread_mutex_lock(&worker_lock);
+ rp->next = work_avail; work_avail = rp;
+ }
+ pthread_mutex_unlock(&worker_lock);
+}
+