long gu_to_self_cancel(gu_to_t *to, gu_seqno_t seqno) { long err = 0; to_waiter_t *w; assert (seqno >= 0); if ((err = gu_mutex_lock (&to->lock))) { gu_fatal("Mutex lock failed (%d): %s", err, strerror(err)); abort(); } if ((w = to_get_waiter (to, seqno)) == NULL) { gu_mutex_unlock(&to->lock); return -EAGAIN; } /* we have a valid waiter now */ if (seqno > to->seqno) { // most probable case w->state = CANCELED; } else if (seqno == to->seqno) { // have to wake the next waiter as if we grabbed and now releasing TO to_release_and_wake_next (to, w); } else { // (seqno < to->seqno) // This waiter must have been canceled or even released by preceding // waiter. Do nothing. } gu_mutex_unlock(&to->lock); return err; }
long gu_to_interrupt (gu_to_t *to, gu_seqno_t seqno) { long rcode = 0; long err; to_waiter_t *w; assert (seqno >= 0); if ((err = gu_mutex_lock (&to->lock))) { gu_fatal("Mutex lock failed (%d): %s", err, strerror(err)); abort(); } if (seqno >= to->seqno) { if ((w = to_get_waiter (to, seqno)) == NULL) { gu_mutex_unlock(&to->lock); return -EAGAIN; } /* we have a valid waiter now */ switch (w->state) { case HOLDER: gu_debug ("trying to interrupt in use seqno: seqno = %llu, " "TO seqno = %llu", seqno, to->seqno); /* gu_mutex_unlock (&to->lock); */ rcode = -ERANGE; break; case CANCELED: gu_debug ("trying to interrupt canceled seqno: seqno = %llu, " "TO seqno = %llu", seqno, to->seqno); /* gu_mutex_unlock (&to->lock); */ rcode = -ERANGE; break; case WAIT: gu_debug ("signaling to interrupt wait seqno: seqno = %llu, " "TO seqno = %llu", seqno, to->seqno); rcode = to_wake_waiter (w); case RELEASED: w->state = INTERRUPTED; break; case INTERRUPTED: gu_debug ("TO waiter interrupt already seqno: seqno = %llu, " "TO seqno = %llu", seqno, to->seqno); break; } } else { gu_debug ("trying to interrupt used seqno: cancel seqno = %llu, " "TO seqno = %llu", seqno, to->seqno); /* gu_mutex_unlock (&to->lock); */ rcode = -ERANGE; } gu_mutex_unlock (&to->lock); return rcode; }
long gu_to_release (gu_to_t *to, gu_seqno_t seqno) { long err; to_waiter_t *w; assert (seqno >= 0); if ((err = gu_mutex_lock(&to->lock))) { gu_fatal("Mutex lock failed (%d): %s", err, strerror(err)); abort(); } if ((w = to_get_waiter (to, seqno)) == NULL) { gu_mutex_unlock(&to->lock); return -EAGAIN; } /* we have a valid waiter now */ if (seqno == to->seqno) { to_release_and_wake_next (to, w); } else if (seqno > to->seqno) { if (w->state != CANCELED) { gu_fatal("Illegal state in premature release: %d", w->state); abort(); } /* Leave state CANCELED so that real releaser can iterate */ } else { /* */ if (w->state != RELEASED) { gu_fatal("Outdated seqno and state not RELEASED: %d", w->state); abort(); } } gu_mutex_unlock(&to->lock); return err; }
long gu_to_destroy (gu_to_t** to) { gu_to_t *t = *to; long ret; ssize_t i; gu_mutex_lock (&t->lock); if (t->used) { gu_mutex_unlock (&t->lock); return -EBUSY; } for (i = 0; i < t->qlen; i++) { to_waiter_t *w = t->queue + i; #ifdef TO_USE_SIGNAL if (gu_cond_destroy (&w->cond)) { // @todo: what if someone is waiting? gu_warn ("Failed to destroy condition %d. Should not happen", i); } #else if (pthread_mutex_destroy (&w->mtx)) { // @todo: what if someone is waiting? gu_warn ("Failed to destroy mutex %d. Should not happen", i); } #endif } t->qlen = 0; gu_mutex_unlock (&t->lock); /* What else can be done here? */ ret = gu_mutex_destroy (&t->lock); if (ret) return -ret; // application can retry gu_free (t->queue); gu_free (t); *to = NULL; return 0; }
void gcs_fifo_lite_open (gcs_fifo_lite_t* fifo) { GCS_FIFO_LITE_LOCK; if (!fifo->closed) { gu_error ("Trying to open an open FIFO."); assert(0); } else { fifo->closed = false; } gu_mutex_unlock(&fifo->lock); }
long gcs_sm_close (gcs_sm_t* sm) { gu_info ("Closing send monitor..."); if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); sm->ret = -EBADFD; if (sm->pause) _gcs_sm_continue_common (sm); gu_cond_t cond; gu_cond_init (&cond, NULL); // in case the queue is full while (sm->users >= (long)sm->wait_q_len) { gu_mutex_unlock (&sm->lock); usleep(1000); gu_mutex_lock (&sm->lock); } while (sm->users > 0) { // wait for cleared queue sm->users++; GCS_SM_INCREMENT(sm->wait_q_tail); _gcs_sm_enqueue_common (sm, &cond); sm->users--; GCS_SM_INCREMENT(sm->wait_q_head); } gu_cond_destroy (&cond); gu_mutex_unlock (&sm->lock); gu_info ("Closed send monitor."); return 0; }
long gu_to_cancel (gu_to_t *to, gu_seqno_t seqno) { long err; to_waiter_t *w; assert (seqno >= 0); if ((err = gu_mutex_lock (&to->lock))) { gu_fatal("Mutex lock failed (%d): %s", err, strerror(err)); abort(); } // Check for queue overflow. This is totally unrecoverable. Abort. if ((w = to_get_waiter (to, seqno)) == NULL) { gu_mutex_unlock(&to->lock); abort(); } /* we have a valid waiter now */ if ((seqno > to->seqno) || (seqno == to->seqno && w->state != HOLDER)) { err = to_wake_waiter (w); w->state = CANCELED; } else if (seqno == to->seqno && w->state == HOLDER) { gu_warn("tried to cancel current TO holder, state %d seqno %llu", w->state, seqno); err = -ECANCELED; } else { gu_warn("trying to cancel used seqno: state %d cancel seqno = %llu, " "TO seqno = %llu", w->state, seqno, to->seqno); err = -ECANCELED; } gu_mutex_unlock (&to->lock); return err; }
void gcs_sm_stats_get (gcs_sm_t* sm, int* q_len, double* q_len_avg, long long* paused_ns, double* paused_avg) { gcs_sm_stats_t tmp; long long now; bool paused; if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); *q_len = sm->users; tmp = sm->stats; now = gu_time_monotonic(); paused = sm->pause; gu_mutex_unlock (&sm->lock); if (paused) { // taking sample in a middle of a pause tmp.paused_ns += now - tmp.pause_start; } *paused_ns = tmp.paused_ns; if (gu_likely(tmp.paused_ns >= 0)) { *paused_avg = ((double)(tmp.paused_ns - tmp.paused_sample)) / (now - tmp.sample_start); } else { *paused_avg = -1.0; } if (gu_likely(tmp.send_q_len >= 0 && tmp.send_q_samples >= 0)){ if (gu_likely(tmp.send_q_samples > 0)) { *q_len_avg = ((double)tmp.send_q_len) / tmp.send_q_samples; } else { *q_len_avg = 0.0; } } else { *q_len_avg = -1.0; } }
long gcs_sm_open (gcs_sm_t* sm) { long ret = -1; if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); if (-EBADFD == sm->ret) /* closed */ { sm->ret = 0; } ret = sm->ret; gu_mutex_unlock (&sm->lock); if (ret) { gu_error ("Can't open send monitor: wrong state %d", ret); } return ret; }
void gcs_fifo_lite_close (gcs_fifo_lite_t* fifo) { GCS_FIFO_LITE_LOCK; if (fifo->closed) { gu_error ("Trying to close a closed FIFO"); assert(0); } else { fifo->closed = true; // wake whoever is waiting fifo->put_wait = 0; gu_cond_broadcast (&fifo->put_cond); fifo->get_wait = 0; gu_cond_broadcast (&fifo->get_cond); } gu_mutex_unlock (&fifo->lock); }
void gcs_sm_stats_flush(gcs_sm_t* sm) { if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); long long const now = gu_time_monotonic(); sm->stats.sample_start = now; sm->stats.paused_sample = sm->stats.paused_ns; if (sm->pause) // append elapsed pause time { sm->stats.paused_sample += now - sm->stats.pause_start; } sm->stats.send_q_len = 0; sm->stats.send_q_samples = 0; gu_mutex_unlock (&sm->lock); }
long gu_to_grab (gu_to_t* to, gu_seqno_t seqno) { long err; to_waiter_t *w; assert (seqno >= 0); if ((err = gu_mutex_lock(&to->lock))) { gu_fatal("Mutex lock failed (%d): %s", err, strerror(err)); abort(); } if (seqno < to->seqno) { gu_mutex_unlock(&to->lock); return -ECANCELED; } if ((w = to_get_waiter (to, seqno)) == NULL) { gu_mutex_unlock(&to->lock); return -EAGAIN; } /* we have a valid waiter now */ switch (w->state) { case INTERRUPTED: w->state = RELEASED; err = -EINTR; break; case CANCELED: err = -ECANCELED; break; case RELEASED: if (seqno == to->seqno) { w->state = HOLDER; } else if (seqno < to->seqno) { gu_error("Trying to grab outdated seqno"); err = -ECANCELED; } else { /* seqno > to->seqno, wait for my turn */ w->state = WAIT; to->used++; #ifdef TO_USE_SIGNAL gu_cond_wait(&w->cond, &to->lock); #else pthread_mutex_lock (&w->mtx); pthread_mutex_unlock (&to->lock); pthread_mutex_lock (&w->mtx); // wait for unlock by other thread pthread_mutex_lock (&to->lock); pthread_mutex_unlock (&w->mtx); #endif to->used--; switch (w->state) { case WAIT:// should be most probable assert (seqno == to->seqno); w->state = HOLDER; break; case INTERRUPTED: w->state = RELEASED; err = -EINTR; break; case CANCELED: err = -ECANCELED; break; case RELEASED: /* this waiter has been cancelled */ assert(seqno < to->seqno); err = -ECANCELED; break; default: gu_fatal("Invalid cond wait exit state %d, seqno %llu(%llu)", w->state, seqno, to->seqno); abort(); } } break; default: gu_fatal("TO queue over wrap"); abort(); } gu_mutex_unlock(&to->lock); return err; }
long gcs_fifo_lite_destroy (gcs_fifo_lite_t* f) { if (f) { if (gu_mutex_lock (&f->lock)) { abort(); } if (f->destroyed) { gu_mutex_unlock (&f->lock); return -EALREADY; } f->closed = true; f->destroyed = true; /* get rid of "put" threads waiting for lock or signal */ while (pthread_cond_destroy (&f->put_cond)) { if (f->put_wait <= 0) { gu_fatal ("Can't destroy condition while nobody's waiting"); abort(); } f->put_wait = 0; gu_cond_broadcast (&f->put_cond); } while (f->used) { /* there are some items in FIFO - and that means * no gcs_fifo_lite_safe_get() is waiting on condition */ gu_mutex_unlock (&f->lock); /* let them get remaining items from FIFO, * we don't know how to deallocate them ourselves. * unfortunately this may take some time */ usleep (10000); /* sleep a bit to avoid busy loop */ gu_mutex_lock (&f->lock); } f->length = 0; /* now all we have - "get" threads waiting for lock or signal */ while (pthread_cond_destroy (&f->get_cond)) { if (f->get_wait <= 0) { gu_fatal ("Can't destroy condition while nobody's waiting"); abort(); } f->get_wait = 0; gu_cond_broadcast (&f->get_cond); } /* at this point there are only functions waiting for lock */ gu_mutex_unlock (&f->lock); while (gu_mutex_destroy (&f->lock)) { /* this should be fast provided safe get and safe put are * wtitten correctly. They should immediately freak out. */ gu_mutex_lock (&f->lock); gu_mutex_unlock (&f->lock); } /* now nobody's waiting for anything */ gu_free (f->queue); gu_free (f); return 0; } return -EINVAL; }