void hlt_thread_queue_flush(hlt_thread_queue* queue, int writer) { int block; batch* b = queue->writer_batches[writer]; if ( ! ( b && b->write_pos ) ) // Nothing to do. return; do { int i; _acquire_lock(queue, &i, 0, writer); ++queue->writer_stats[writer].locked; if ( ! queue->lock_block ) { block = 0; // If we have space in the queue, move our batch to the end of // the reader's pending queue. if ( queue->lock_num_pending < queue->max_batches || ! queue->max_batches ) { if ( queue->lock_pending_tail ) queue->lock_pending_tail->next = b; else queue->lock_pending_head = b; queue->lock_pending_tail = b; ++queue->lock_num_pending; ++queue->writer_stats[writer].batches; // Can't write to this batch any longer. queue->writer_batches[writer] = 0; } else // Max number of pending batches reached, can't do anything right now. queue->lock_block = block = 1; } else // Oops, pending queue is completely filled up already. Need to // block. block = 1; _release_lock(queue, i, 0, writer); if ( block ) { ++queue->writer_stats[writer].blocked; // Sleep a tiny bit. // pthread_yield(); hlt_util_nanosleep(1000); } pthread_testcancel(); } while ( block ); assert(! queue->writer_batches[writer]); }
void hlt_thread_queue_terminate_writer(hlt_thread_queue* queue, int writer) { int s; _acquire_lock(queue, &s, 0, writer); queue->lock_writers_terminated[writer] = 1; _release_lock(queue, s, 0, writer); hlt_thread_queue_flush(queue, writer); }
static PyObject * release_lock(PyObject *self) { (void)self; if (!_release_lock()) { PyErr_SetString(PyExc_RuntimeError, "Thread lock count is zero"); return NULL; } Py_RETURN_NONE; }
static void _debug_print_queue(const char *prefix, hlt_thread_queue* q, int reader, int thread) { int s; _acquire_lock(q, &s, reader, thread); fprintf(stderr, "%s: %p\n", prefix, q); for ( int i = 0; i < q->writers; i++ ) { fprintf(stderr, " writer %d (elems %lu, terminated %d) * ", i, q->writer_num_written[i], q->lock_writers_terminated[i]); _debug_print_batch(q->writer_batches[i]); } fprintf(stderr, " reader at %d (elems %lu, found terminated %d) * ", q->reader_pos, q->reader_num_read, q->reader_num_terminated); _debug_print_batch(q->reader_head); fprintf(stderr, " pending (num %d / block %d) * ", q->lock_num_pending, q->lock_block); _debug_print_batch(q->lock_pending_head); _release_lock(q, s, reader, thread); }
void* hlt_thread_queue_read(hlt_thread_queue* queue, int timeout) { int block = (timeout == 0); timeout *= 1000; // Turn it into nanoseconds. while ( 1 ) { while ( queue->reader_head ) { // We still have stuff to do, so do it. if ( queue->need_flush ) queue->need_flush = 0; batch* b = queue->reader_head; if ( queue->reader_pos < b->write_pos ) { // Still something in our current batch. ++queue->reader_stats->elems; ++queue->reader_num_read; return b->elems[queue->reader_pos++]; } // Switch to next batch. batch* next = queue->reader_head->next; hlt_free(queue->reader_head); queue->reader_head = next; queue->reader_pos = 0; ++queue->reader_stats->batches; } pthread_testcancel(); // Nothing left anymore, get a currently pending batch. int s; _acquire_lock(queue, &s, 1, 0); ++queue->reader_stats->locked; queue->reader_head = queue->lock_pending_head; queue->reader_pos = 0; queue->lock_pending_head = queue->lock_pending_tail = 0; queue->lock_num_pending = 0; queue->lock_block = 0; // Take the opportunity to check who has terminated. queue->reader_num_terminated = 0; for ( int i = 0; i < queue->writers; ++i ) { if ( queue->lock_writers_terminated[i] ) ++queue->reader_num_terminated; } _release_lock(queue, s, 1, 0); if ( ! queue->reader_head ) { // Nothing was pending actually ... ++queue->reader_stats->blocked; queue->need_flush = 1; if ( timeout <= 0 && ! block ) return 0; if ( hlt_thread_queue_terminated(queue) ) return 0; // Sleep a tiny bit. // pthread_yield(); hlt_util_nanosleep(1000); timeout -= 1000; } } // Can't be reached. assert(0); }