/** * If a GNUNET_DNS_RequestHandler calls this function, the request is * supposed to be answered with the data provided to this call (with * the modifications the function might have made). * * @param rh request that should now be answered * @param reply_length size of reply (uint16_t to force sane size) * @param reply reply data */ void GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh, uint16_t reply_length, const char *reply) { struct ReplyQueueEntry *qe; struct GNUNET_DNS_Response *resp; GNUNET_assert (0 < rh->dh->pending_requests--); if (rh->generation != rh->dh->generation) { GNUNET_free (rh); return; } if (reply_length + sizeof (struct GNUNET_DNS_Response) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) { GNUNET_break (0); GNUNET_free (rh); return; } qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) + sizeof (struct GNUNET_DNS_Response) + reply_length); resp = (struct GNUNET_DNS_Response*) &qe[1]; qe->msg = &resp->header; resp->header.size = htons (sizeof (struct GNUNET_DNS_Response) + reply_length); resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE); resp->drop_flag = htonl (2); resp->request_id = rh->request_id; memcpy (&resp[1], reply, reply_length); queue_reply (rh->dh, qe); GNUNET_free (rh); }
/** * Main function for the worker thread. */ static void worker_thread_main(void *thread_) { workerthread_t *thread = thread_; threadpool_t *pool = thread->in_pool; workqueue_entry_t *work; workqueue_reply_t result; tor_mutex_acquire(&pool->lock); while (1) { /* lock must be held at this point. */ while (worker_thread_has_work(thread)) { /* lock must be held at this point. */ if (thread->in_pool->generation != thread->generation) { void *arg = thread->in_pool->update_args[thread->index]; thread->in_pool->update_args[thread->index] = NULL; workqueue_reply_t (*update_fn)(void*,void*) = thread->in_pool->update_fn; thread->generation = thread->in_pool->generation; tor_mutex_release(&pool->lock); workqueue_reply_t r = update_fn(thread->state, arg); if (r != WQ_RPL_REPLY) { return; } tor_mutex_acquire(&pool->lock); continue; } work = TOR_TAILQ_FIRST(&pool->work); TOR_TAILQ_REMOVE(&pool->work, work, next_work); work->pending = 0; tor_mutex_release(&pool->lock); /* We run the work function without holding the thread lock. This * is the main thread's first opportunity to give us more work. */ result = work->fn(thread->state, work->arg); /* Queue the reply for the main thread. */ queue_reply(thread->reply_queue, work); /* We may need to exit the thread. */ if (result != WQ_RPL_REPLY) { return; } tor_mutex_acquire(&pool->lock); } /* At this point the lock is held, and there is no work in this thread's * queue. */ /* TODO: support an idle-function */ /* Okay. Now, wait till somebody has work for us. */ if (tor_cond_wait(&pool->condition, &pool->lock, NULL) < 0) { log_warn(LD_GENERAL, "Fail tor_cond_wait."); } } }
/** * Reconnect to the DNS service. * * @param cls handle with the connection to connect * @param tc scheduler context (unused) */ static void reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_DNS_Handle *dh = cls; struct ReplyQueueEntry *qe; struct GNUNET_DNS_Register *msg; dh->reconnect_task = NULL; dh->dns_connection = GNUNET_CLIENT_connect ("dns", dh->cfg); if (NULL == dh->dns_connection) return; dh->generation++; qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) + sizeof (struct GNUNET_DNS_Register)); msg = (struct GNUNET_DNS_Register*) &qe[1]; qe->msg = &msg->header; msg->header.size = htons (sizeof (struct GNUNET_DNS_Register)); msg->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT); msg->flags = htonl (dh->flags); queue_reply (dh, qe); }
/** * If a GNUNET_DNS_RequestHandler calls this function, the request is * to be dropped and no response should be generated. * * @param rh request that should now be dropped */ void GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh) { struct ReplyQueueEntry *qe; struct GNUNET_DNS_Response *resp; GNUNET_assert (0 < rh->dh->pending_requests--); if (rh->generation != rh->dh->generation) { GNUNET_free (rh); return; } qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) + sizeof (struct GNUNET_DNS_Response)); resp = (struct GNUNET_DNS_Response*) &qe[1]; qe->msg = &resp->header; resp->header.size = htons (sizeof (struct GNUNET_DNS_Response)); resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE); resp->request_id = rh->request_id; resp->drop_flag = htonl (0); queue_reply (rh->dh, qe); GNUNET_free (rh); }
/* Voice thread message processing */ static enum voice_state voice_message(struct voice_thread_data *td) { if (quiet_counter > 0) queue_wait_w_tmo(&voice_queue, &td->ev, HZ/10); else queue_wait(&voice_queue, &td->ev); switch (td->ev.id) { case Q_VOICE_PLAY: LOGFQUEUE("voice < Q_VOICE_PLAY"); if (quiet_counter == 0) { /* Boost CPU now */ trigger_cpu_boost(); } else { /* Stop any clip still playing */ voice_stop_playback(); } quiet_counter = QUIET_COUNT; /* Copy the clip info */ td->vi = *(struct voice_info *)td->ev.data; /* Be sure audio buffer is initialized */ audio_restore_playback(AUDIO_WANT_VOICE); /* We need nothing more from the sending thread - let it run */ queue_reply(&voice_queue, 1); /* Make audio play more softly and set delay to return to normal playback level */ pcmbuf_soft_mode(true); /* Clean-start the decoder */ td->st = speex_decoder_init(&speex_wb_mode); /* Make bit buffer use our own buffer */ speex_bits_set_bit_buffer(&td->bits, td->vi.start, td->vi.size); speex_decoder_ctl(td->st, SPEEX_GET_LOOKAHEAD, &td->lookahead); return VOICE_STATE_DECODE; case SYS_TIMEOUT: if (voice_unplayed_frames()) { /* Waiting for PCM to finish */ break; } /* Drop through and stop the first time after clip runs out */ if (quiet_counter-- != QUIET_COUNT) { if (quiet_counter <= 0) pcmbuf_soft_mode(false); break; } /* Fall-through */ case Q_VOICE_STOP: LOGFQUEUE("voice < Q_VOICE_STOP"); cancel_cpu_boost(); voice_stop_playback(); break; /* No default: no other message ids are sent */ } return VOICE_STATE_MESSAGE; }
/** CODEC THREAD */ static void codec_thread(void) { struct queue_event ev; int status; while (1) { status = 0; #ifdef HAVE_CROSSFADE if (!pcmbuf_is_crossfade_active()) #endif { cancel_cpu_boost(); } queue_wait(&codec_queue, &ev); codec_requested_stop = false; switch (ev.id) { case Q_CODEC_LOAD_DISK: LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); queue_reply(&codec_queue, 1); audio_codec_loaded = true; ci.stop_codec = false; status = codec_load_file((const char *)ev.data, &ci); LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev.data, status); break; case Q_CODEC_LOAD: LOGFQUEUE("codec < Q_CODEC_LOAD"); if (*get_codec_hid() < 0) { logf("Codec slot is empty!"); /* Wait for the pcm buffer to go empty */ while (pcm_is_playing()) yield(); /* This must be set to prevent an infinite loop */ ci.stop_codec = true; LOGFQUEUE("codec > codec Q_AUDIO_PLAY"); queue_post(&codec_queue, Q_AUDIO_PLAY, 0); break; } audio_codec_loaded = true; ci.stop_codec = false; status = codec_load_buf(*get_codec_hid(), &ci); LOGFQUEUE("codec_load_buf %d\n", status); break; case Q_CODEC_DO_CALLBACK: LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK"); queue_reply(&codec_queue, 1); if ((void*)ev.data != NULL) { cpucache_invalidate(); ((void (*)(void))ev.data)(); cpucache_flush(); } break; #ifdef AUDIO_HAVE_RECORDING case Q_ENCODER_LOAD_DISK: LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK"); audio_codec_loaded = false; /* Not audio codec! */ logf("loading encoder"); ci.stop_encoder = false; status = codec_load_file((const char *)ev.data, &ci); logf("encoder stopped"); break; #endif /* AUDIO_HAVE_RECORDING */ default: LOGFQUEUE("codec < default"); } if (audio_codec_loaded) { if (ci.stop_codec) { status = CODEC_OK; if (!(audio_status() & AUDIO_STATUS_PLAY)) pcmbuf_play_stop(); } audio_codec_loaded = false; } switch (ev.id) { case Q_CODEC_LOAD_DISK: case Q_CODEC_LOAD: LOGFQUEUE("codec < Q_CODEC_LOAD"); if (audio_status() & AUDIO_STATUS_PLAY) { if (ci.new_track || status != CODEC_OK) { if (!ci.new_track) { logf("Codec failure, %d %d", ci.new_track, status); splash(HZ*2, "Codec failure"); } if (!codec_load_next_track()) { LOGFQUEUE("codec > audio Q_AUDIO_STOP"); /* End of playlist */ queue_post(&audio_queue, Q_AUDIO_STOP, 0); break; } } else { logf("Codec finished"); if (ci.stop_codec) { /* Wait for the audio to stop playing before * triggering the WPS exit */ while(pcm_is_playing()) { /* There has been one too many struct pointer swaps by now * so even though it says othertrack_id3, its the correct one! */ othertrack_id3->elapsed = othertrack_id3->length - pcmbuf_get_latency(); sleep(1); } if (codec_requested_stop) { LOGFQUEUE("codec > audio Q_AUDIO_STOP"); queue_post(&audio_queue, Q_AUDIO_STOP, 0); } break; } } if (*get_codec_hid() >= 0) { LOGFQUEUE("codec > codec Q_CODEC_LOAD"); queue_post(&codec_queue, Q_CODEC_LOAD, 0); } else { const char *codec_fn = get_codec_filename(thistrack_id3->codectype); if (codec_fn) { LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); queue_post(&codec_queue, Q_CODEC_LOAD_DISK, (intptr_t)codec_fn); } } } break; #ifdef AUDIO_HAVE_RECORDING case Q_ENCODER_LOAD_DISK: LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK"); if (status == CODEC_OK) break; logf("Encoder failure"); splash(HZ*2, "Encoder failure"); if (ci.enc_codec_loaded < 0) break; logf("Encoder failed to load"); ci.enc_codec_loaded = -1; break; #endif /* AUDIO_HAVE_RECORDING */ default: LOGFQUEUE("codec < default"); } /* end switch */ } }
/* static routines */ static void codec_queue_ack(intptr_t ackme) { queue_reply(&codec_queue, ackme); }
void buffering_thread(void) { bool filling = false; struct queue_event ev; while (true) { if (!filling) { cancel_cpu_boost(); } queue_wait_w_tmo(&buffering_queue, &ev, filling ? 5 : HZ/2); switch (ev.id) { case Q_START_FILL: LOGFQUEUE("buffering < Q_START_FILL %d", (int)ev.data); /* Call buffer callbacks here because this is one of two ways * to begin a full buffer fill */ send_event(BUFFER_EVENT_BUFFER_LOW, 0); shrink_buffer(); queue_reply(&buffering_queue, 1); filling |= buffer_handle((int)ev.data); break; case Q_BUFFER_HANDLE: LOGFQUEUE("buffering < Q_BUFFER_HANDLE %d", (int)ev.data); queue_reply(&buffering_queue, 1); buffer_handle((int)ev.data); break; case Q_RESET_HANDLE: LOGFQUEUE("buffering < Q_RESET_HANDLE %d", (int)ev.data); queue_reply(&buffering_queue, 1); reset_handle((int)ev.data); break; case Q_CLOSE_HANDLE: LOGFQUEUE("buffering < Q_CLOSE_HANDLE %d", (int)ev.data); queue_reply(&buffering_queue, close_handle((int)ev.data)); break; case Q_HANDLE_ADDED: LOGFQUEUE("buffering < Q_HANDLE_ADDED %d", (int)ev.data); /* A handle was added: the disk is spinning, so we can fill */ filling = true; break; case Q_BASE_HANDLE: LOGFQUEUE("buffering < Q_BASE_HANDLE %d", (int)ev.data); base_handle_id = (int)ev.data; break; #ifndef SIMULATOR case SYS_USB_CONNECTED: LOGFQUEUE("buffering < SYS_USB_CONNECTED"); usb_acknowledge(SYS_USB_CONNECTED_ACK); usb_wait_for_disconnect(&buffering_queue); break; #endif case SYS_TIMEOUT: LOGFQUEUE_SYS_TIMEOUT("buffering < SYS_TIMEOUT"); break; } update_data_counters(); /* If the buffer is low, call the callbacks to get new data */ if (num_handles > 0 && data_counters.useful <= conf_watermark) send_event(BUFFER_EVENT_BUFFER_LOW, 0); #if 0 /* TODO: This needs to be fixed to use the idle callback, disable it * for simplicity until its done right */ #if MEM > 8 /* If the disk is spinning, take advantage by filling the buffer */ else if (storage_disk_is_active() && queue_empty(&buffering_queue)) { if (num_handles > 0 && data_counters.useful <= high_watermark) send_event(BUFFER_EVENT_BUFFER_LOW, 0); if (data_counters.remaining > 0 && BUF_USED <= high_watermark) { /* This is a new fill, shrink the buffer up first */ if (!filling) shrink_buffer(); filling = fill_buffer(); update_data_counters(); } } #endif #endif if (queue_empty(&buffering_queue)) { if (filling) { if (data_counters.remaining > 0 && BUF_USED < buffer_len) filling = fill_buffer(); else if (data_counters.remaining == 0) filling = false; } else if (ev.id == SYS_TIMEOUT) { if (data_counters.remaining > 0 && data_counters.useful <= conf_watermark) { shrink_buffer(); filling = fill_buffer(); } } } } }
/* Voice thread message processing */ static void voice_message(struct voice_thread_data *td) { while (1) { switch (td->ev.id) { case Q_VOICE_PLAY: LOGFQUEUE("voice < Q_VOICE_PLAY"); /* Put up a block for completion signal */ voice_done = false; /* Copy the clip info */ td->vi = *(struct voice_info *)td->ev.data; /* Be sure audio buffer is initialized */ audio_restore_playback(AUDIO_WANT_VOICE); /* We need nothing more from the sending thread - let it run */ queue_reply(&voice_queue, 1); if (td->state == TSTATE_STOPPED) { /* Boost CPU now */ trigger_cpu_boost(); } else if (!playback_is_playing()) { /* Just voice, stop any clip still playing */ pcmbuf_play_stop(); } /* Clean-start the decoder */ td->st = speex_decoder_init(&speex_wb_mode); /* Make bit buffer use our own buffer */ speex_bits_set_bit_buffer(&td->bits, td->vi.start, td->vi.size); speex_decoder_ctl(td->st, SPEEX_GET_LOOKAHEAD, &td->lookahead); td->state = TSTATE_DECODE; return; case Q_VOICE_STOP: LOGFQUEUE("voice < Q_VOICE_STOP: %ld", td->ev.data); if (td->ev.data != 0 && !playback_is_playing()) { /* If not playing, it's just voice so stop pcm playback */ pcmbuf_play_stop(); } /* Cancel boost */ cancel_cpu_boost(); td->state = TSTATE_STOPPED; voice_done = true; break; case Q_VOICE_STATE: LOGFQUEUE("voice < Q_VOICE_STATE"); queue_reply(&voice_queue, td->state); if (td->state == TSTATE_STOPPED) break; /* Not in a playback state */ return; default: /* Default messages get a reply and thread continues with no * state transition */ LOGFQUEUE("voice < default"); if (td->state == TSTATE_STOPPED) break; /* Not in playback state */ queue_reply(&voice_queue, 0); return; } queue_wait(&voice_queue, &td->ev); } }