int iob_tryadd_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq) { FAR struct iob_qentry_s *qentry; /* Allocate a container to hold the I/O buffer chain */ qentry = iob_tryalloc_qentry(); if (!qentry) { ndbg("ERROR: Failed to allocate a container\n"); return -ENOMEM; } return iob_add_queue_internal(iob, iobq, qentry); }
FAR struct iob_qentry_s *iob_alloc_qentry(void) { /* Were we called from the interrupt level? */ if (up_interrupt_context()) { /* Yes, then try to allocate an I/O buffer without waiting */ return iob_tryalloc_qentry(); } else { /* Then allocate an I/O buffer, waiting as necessary */ return iob_allocwait_qentry(); } }
static FAR struct iob_qentry_s *iob_allocwait_qentry(void) { FAR struct iob_qentry_s *qentry; irqstate_t flags; int ret; /* The following must be atomic; interrupt must be disabled so that there * is no conflict with interrupt level I/O buffer chain container * allocations. This is not as bad as it sounds because interrupts will be * re-enabled while we are waiting for I/O buffers to become free. */ flags = irqsave(); do { /* Try to get an I/O buffer chain container. If successful, the * semaphore count will be decremented atomically. */ qentry = iob_tryalloc_qentry(); if (!qentry) { /* If not successful, then the semaphore count was less than or * equal to zero (meaning that there are no free buffers). We * need to wait for an I/O buffer chain container to be released * when the semaphore count will be incremented. */ ret = sem_wait(&g_qentry_sem); /* When we wake up from wait, an I/O buffer chain container was * returned to the free list. However, if there are concurrent * allocations from interrupt handling, then I suspect that there * is a race condition. But no harm, we will just wait again in * that case. */ } } while (ret == OK && !qentry); irqrestore(flags); return qentry; }
static FAR struct iob_qentry_s *iob_allocwait_qentry(void) { FAR struct iob_qentry_s *qentry; irqstate_t flags; int ret = OK; /* The following must be atomic; interrupt must be disabled so that there * is no conflict with interrupt level I/O buffer chain container * allocations. This is not as bad as it sounds because interrupts will be * re-enabled while we are waiting for I/O buffers to become free. */ flags = enter_critical_section(); do { /* Try to get an I/O buffer chain container. If successful, the * semaphore count will be decremented atomically. */ qentry = iob_tryalloc_qentry(); if (!qentry) { /* If not successful, then the semaphore count was less than or * equal to zero (meaning that there are no free buffers). We * need to wait for an I/O buffer chain container to be released * when the semaphore count will be incremented. */ ret = sem_wait(&g_qentry_sem); if (ret < 0) { int errcode = get_errno(); /* EINTR is not an error! EINTR simply means that we were * awakened by a signal and we should try again. * * REVISIT: Many end-user interfaces are required to return * with an error if EINTR is set. Most uses of this function * is in internal, non-user logic. But are there cases where * the error should be returned. */ if (errcode == EINTR) { /* Force a success indication so that we will continue * looping. */ ret = 0; } else { /* Stop the loop and return a error */ DEBUGASSERT(errcode > 0); ret = -errcode; } } else { /* When we wake up from wait successfully, an I/O buffer chain * container was returned to the free list. However, if there * are concurrent allocations from interrupt handling, then I * suspect that there is a race condition. But no harm, we * will just wait again in that case. * * We need release our count so that it is available to * iob_tryalloc_qentry(), perhaps allowing another thread to * take our count. In that event, iob_tryalloc_qentry() will * fail above and we will have to wait again. * * TODO: Consider a design modification to permit us to * complete the allocation without losing our count. */ sem_post(&g_qentry_sem); } } } while (ret == OK && !qentry); leave_critical_section(flags); return qentry; }