void *simple_thread_req(thread_t *th, void *req) { void *rpl; simple_mutex_lock(th->mut); th->req = req; simple_sem_post(th->sem_req); // valid simple_sem_wait(th->sem_rpl); // wait rpl to valid rpl = th->rpl; simple_mutex_unlock(th->mut); return rpl; }
int destroy_ring_buffer (buffer_t *buffer) { assert (buffer != NULL); zlog_category_t *category = zlog_get_category ("ring_buffer"); assert (category != NULL); buffer->shutdown = true; zlog_debug (category, "Destroying ring buffer."); int status; // NOTES: Destroy any user before destroy ring buffer! //status = pthread_mutex_lock (&buffer->mutex); //assert (status == 0); zlog_debug (category, "Destroying mutex."); simple_cond_broadcast (&buffer->cond_not_full); status = simple_cond_destroy (&buffer->cond_not_full); assert (status == 0); simple_cond_broadcast (&buffer->cond_not_empty); status = simple_cond_destroy (&buffer->cond_not_empty); assert (status == 0); status = simple_mutex_unlock (&buffer->mutex); assert (status == 0); status = simple_mutex_destroy (&buffer->mutex); while (status != 0) { status = simple_mutex_destroy (&buffer->mutex); } assert (status == 0); zlog_debug (category, "Reclaiming memory."); assert (&buffer->entries != NULL); free (buffer->entries); return 0; }
buffer_entry_t* _consume (buffer_t *buffer) { assert (buffer != NULL); zlog_category_t *category = zlog_get_category ("ring_buffer"); assert (category != NULL); zlog_debug (category, "Consumer of TID %u", pthread_self ()); // if shutdown if (buffer->shutdown == true) { zlog_debug (category, "Shutting down consumer."); return 0; } // try to lock zlog_debug (category, "Waiting for lock."); int status = simple_mutex_lock (&buffer->mutex); assert (status == 0); zlog_debug (category, "Locked."); // wait for non-empty while (buffer->count <= buffer->produce_threshold) { zlog_debug (category, "Buffer threshold not reached. Waiting for producers."); status = simple_cond_wait (&buffer->cond_not_empty, &buffer->mutex); assert (status == 0); // if shutdown if (buffer->shutdown == true) { status = simple_mutex_unlock (&buffer->mutex); assert (status == 0); return 0; } } assert (buffer->count > buffer->produce_threshold); //assert (buffer->count > 0); // allocate entry zlog_debug (category, "Reading from buffer."); buffer_entry_t *entry = _allocate_entry (); memcpy (entry, &(buffer->entries)[buffer->consumer_index], sizeof (buffer_entry_t)); // update zlog_debug (category, "Updating status."); buffer->count--; assert (buffer->count >= 0); buffer->consumer_index = (buffer->consumer_index + 1) % buffer->size; zlog_debug (category, "Count %d, PI %d, CI %d", buffer->count, buffer->producer_index, buffer->consumer_index); // non full if (buffer->count == buffer->produce_threshold) { zlog_debug (category, "Signalling non-full condition."); status = simple_cond_broadcast (&buffer->cond_not_full); assert (status == 0); } // unlock zlog_debug (category, "Finish reading. Unlock."); status = simple_mutex_unlock (&buffer->mutex); assert (status == 0); return entry; }
int _produce (buffer_t *buffer, buffer_entry_t *buffer_entry) { assert (buffer != NULL); assert (buffer_entry != NULL); zlog_category_t *category = zlog_get_category ("ring_buffer"); assert (category != NULL); zlog_debug (category, "Producer of TID %u", pthread_self ()); // if shutdown if (buffer->shutdown == true) { zlog_debug (category, "Shutting down producer."); return 0; } // try to lock buffer zlog_debug (category, "Waiting for lock."); int status = simple_mutex_lock (&buffer->mutex); assert (status == 0); zlog_debug (category, "Locked."); // wait for not full while (buffer->count >= buffer->consume_threshold) { zlog_debug (category, "Buffer threshold not reached. Waiting for consumers."); status = simple_cond_wait (&buffer->cond_not_full, &buffer->mutex); assert (status == 0); // if shutdown if (buffer->shutdown == true) { status = simple_mutex_unlock (&buffer->mutex); assert (status == 0); return 0; } } assert (buffer->count < buffer->consume_threshold); //assert (buffer->count < buffer->size); // copy entry to buffer zlog_debug (category, "Writing to buffer."); memcpy (&(buffer->entries)[buffer->producer_index], buffer_entry, sizeof (buffer_entry_t)); // update count zlog_debug (category, "Updating status."); buffer->count++; assert (buffer->count <= buffer->size); buffer->producer_index = (buffer->producer_index + 1) % buffer->size; zlog_debug (category, "Count %d, PI %d, CI %d", buffer->count, buffer->producer_index, buffer->consumer_index); // signal if (buffer->count == buffer->consume_threshold) { zlog_debug (category, "Signalling non-empty condition."); status = simple_cond_broadcast (&buffer->cond_not_empty); assert (status == 0); } // unlock zlog_debug (category, "Finish writing. Unlock."); status = simple_mutex_unlock (&buffer->mutex); assert (status == 0); return 0; }
/** * The main polling loop * * This routine does the polling and despatches of IO events * to the DCB's. It may be called either directly or as the entry point * of a polling thread within the gateway. * * The routine will loop as long as the variable "shutdown" is set to zero, * setting this to a non-zero value will cause the polling loop to return. * * There are two options for the polling, a debug option that is only useful if * you have a single thread. This blocks in epoll_wait until an event occurs. * * The non-debug option does an epoll with a time out. This allows the checking of * shutdown value to be checked in all threads. The algorithm for polling in this * mode is to do a poll with no-wait, if no events are detected then the poll is * repeated with a time out. This allows for a quick check before making the call * with timeout. The call with the timeout differs in that the Linux scheduler may * deschedule a process if a timeout is included, but will not do this if a 0 timeout * value is given. this improves performance when the gateway is under heavy load. * * In order to provide a fairer means of sharing the threads between the different * DCB's the poll mechanism has been decoupled from the processing of the events. * The events are now recieved via the epoll_wait call, a queue of DCB's that have * events pending is maintained and as new events arrive the DCB is added to the end * of this queue. If an eent arrives for a DCB alreayd in the queue, then the event * bits are added to the DCB but the DCB mantains the same point in the queue unless * the original events are already being processed. If they are being processed then * the DCB is moved to the back of the queue, this means that a DCB that is receiving * events at a high rate will not block the execution of events for other DCB's and * should result in a fairer polling strategy. * * The introduction of the ability to inject "fake" write events into the event queue meant * that there was a possibility to "starve" new events sicne the polling loop would * consume the event queue before looking for new events. If the DCB that inject * the fake event then injected another fake event as a result of the first it meant * that new events did not get added to the queue. The strategy has been updated to * not consume the entire event queue, but process one event before doing a non-blocking * call to add any new events before processing any more events. A blocking call to * collect events is only made if there are no pending events to be processed on the * event queue. * * Also introduced a "timeout bias" mechanism. This mechansim control the length of * of timeout passed to epoll_wait in blocking calls based on previous behaviour. * The initial call will block for 10% of the define timeout peroid, this will be * increased in increments of 10% until the full timeout value is used. If at any * point there is an event to be processed then the value will be reduced to 10% again * for the next blocking call. * * @param arg The thread ID passed as a void * to satisfy the threading package */ void poll_waitevents(void *arg) { struct epoll_event events[MAX_EVENTS]; int i, nfds, timeout_bias = 1; int thread_id = (int)arg; DCB *zombies = NULL; int poll_spins = 0; /** Add this thread to the bitmask of running polling threads */ bitmask_set(&poll_mask, thread_id); if (thread_data) { thread_data[thread_id].state = THREAD_IDLE; } /** Init mysql thread context for use with a mysql handle and a parser */ mysql_thread_init(); while (1) { if (pollStats.evq_pending == 0 && timeout_bias < 10) { timeout_bias++; } atomic_add(&n_waiting, 1); #if BLOCKINGPOLL nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); atomic_add(&n_waiting, -1); #else /* BLOCKINGPOLL */ #if MUTEX_EPOLL simple_mutex_lock(&epoll_wait_mutex, TRUE); #endif if (thread_data) { thread_data[thread_id].state = THREAD_POLLING; } atomic_add(&pollStats.n_polls, 1); if ((nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, 0)) == -1) { atomic_add(&n_waiting, -1); int eno = errno; errno = 0; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [poll_waitevents] epoll_wait returned " "%d, errno %d", pthread_self(), nfds, eno))); atomic_add(&n_waiting, -1); } /* * If there are no new descriptors from the non-blocking call * and nothing to process on the event queue then for do a * blocking call to epoll_wait. * * We calculate a timeout bias to alter the length of the blocking * call based on the time since we last received an event to process */ else if (nfds == 0 && pollStats.evq_pending == 0 && poll_spins++ > number_poll_spins) { atomic_add(&pollStats.blockingpolls, 1); nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, (max_poll_sleep * timeout_bias) / 10); if (nfds == 0 && pollStats.evq_pending) { atomic_add(&pollStats.wake_evqpending, 1); poll_spins = 0; } } else { atomic_add(&n_waiting, -1); } if (n_waiting == 0) atomic_add(&pollStats.n_nothreads, 1); #if MUTEX_EPOLL simple_mutex_unlock(&epoll_wait_mutex); #endif #endif /* BLOCKINGPOLL */ if (nfds > 0) { timeout_bias = 1; if (poll_spins <= number_poll_spins + 1) atomic_add(&pollStats.n_nbpollev, 1); poll_spins = 0; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [poll_waitevents] epoll_wait found %d fds", pthread_self(), nfds))); atomic_add(&pollStats.n_pollev, 1); if (thread_data) { thread_data[thread_id].n_fds = nfds; thread_data[thread_id].cur_dcb = NULL; thread_data[thread_id].event = 0; thread_data[thread_id].state = THREAD_PROCESSING; } pollStats.n_fds[(nfds < MAXNFDS ? (nfds - 1) : MAXNFDS - 1)]++; load_average = (load_average * load_samples + nfds) / (load_samples + 1); atomic_add(&load_samples, 1); atomic_add(&load_nfds, nfds); /* * Process every DCB that has a new event and add * it to the poll queue. * If the DCB is currently being processed then we * or in the new eent bits to the pending event bits * and leave it in the queue. * If the DCB was not already in the queue then it was * idle and is added to the queue to process after * setting the event bits. */ for (i = 0; i < nfds; i++) { DCB *dcb = (DCB *)events[i].data.ptr; __uint32_t ev = events[i].events; spinlock_acquire(&pollqlock); if (DCB_POLL_BUSY(dcb)) { if (dcb->evq.pending_events == 0) { pollStats.evq_pending++; dcb->evq.inserted = hkheartbeat; } dcb->evq.pending_events |= ev; } else { dcb->evq.pending_events = ev; if (eventq) { dcb->evq.prev = eventq->evq.prev; eventq->evq.prev->evq.next = dcb; eventq->evq.prev = dcb; dcb->evq.next = eventq; } else { eventq = dcb; dcb->evq.prev = dcb; dcb->evq.next = dcb; } pollStats.evq_length++; pollStats.evq_pending++; dcb->evq.inserted = hkheartbeat; if (pollStats.evq_length > pollStats.evq_max) { pollStats.evq_max = pollStats.evq_length; } } spinlock_release(&pollqlock); } } /* * Process of the queue of waiting requests * This is done without checking the evq_pending count as a * precautionary measure to avoid issues if the house keeping * of the count goes wrong. */ if (process_pollq(thread_id)) timeout_bias = 1; if (thread_data) thread_data[thread_id].state = THREAD_ZPROCESSING; zombies = dcb_process_zombies(thread_id); if (thread_data) thread_data[thread_id].state = THREAD_IDLE; if (do_shutdown) { /*< * Remove the thread from the bitmask of running * polling threads. */ if (thread_data) { thread_data[thread_id].state = THREAD_STOPPED; } bitmask_clear(&poll_mask, thread_id); /** Release mysql thread context */ mysql_thread_end(); return; } if (thread_data) { thread_data[thread_id].state = THREAD_IDLE; } } /*< while(1) */ }