static struct pb_buffer * pb_cache_manager_create_buffer(struct pb_manager *_mgr, pb_size size, const struct pb_desc *desc) { struct pb_cache_manager *mgr = pb_cache_manager(_mgr); struct pb_cache_buffer *buf; struct pb_cache_buffer *curr_buf; struct list_head *curr, *next; int64_t now; pipe_mutex_lock(mgr->mutex); buf = NULL; curr = mgr->delayed.next; next = curr->next; /* search in the expired buffers, freeing them in the process */ now = os_time_get(); while(curr != &mgr->delayed) { curr_buf = LIST_ENTRY(struct pb_cache_buffer, curr, head); if(!buf && pb_cache_is_buffer_compat(curr_buf, size, desc)) buf = curr_buf; else if(os_time_timeout(curr_buf->start, curr_buf->end, now)) _pb_cache_buffer_destroy(curr_buf); else /* This buffer (and all hereafter) are still hot in cache */ break; curr = next; next = curr->next; } /* keep searching in the hot buffers */ if(!buf) { while(curr != &mgr->delayed) { curr_buf = LIST_ENTRY(struct pb_cache_buffer, curr, head); if(pb_cache_is_buffer_compat(curr_buf, size, desc)) { buf = curr_buf; break; } /* no need to check the timeout here */ curr = next; next = curr->next; } } if(buf) { LIST_DEL(&buf->head); pipe_mutex_unlock(mgr->mutex); /* Increase refcount */ pipe_reference_init(&buf->base.base.reference, 1); return &buf->base; } pipe_mutex_unlock(mgr->mutex); buf = CALLOC_STRUCT(pb_cache_buffer); if(!buf) return NULL; buf->buffer = mgr->provider->create_buffer(mgr->provider, size, desc); if(!buf->buffer) { FREE(buf); return NULL; } assert(pipe_is_referenced(&buf->buffer->base.reference)); assert(pb_check_alignment(desc->alignment, buf->buffer->base.alignment)); assert(pb_check_usage(desc->usage, buf->buffer->base.usage)); assert(buf->buffer->base.size >= size); pipe_reference_init(&buf->base.base.reference, 1); buf->base.base.alignment = buf->buffer->base.alignment; buf->base.base.usage = buf->buffer->base.usage; buf->base.base.size = buf->buffer->base.size; buf->base.vtbl = &pb_cache_buffer_vtbl; buf->mgr = mgr; return &buf->base; }
/** * Find a compatible buffer in the cache, return it, and remove it * from the cache. */ struct pb_buffer * pb_cache_reclaim_buffer(struct pb_cache *mgr, pb_size size, unsigned alignment, unsigned usage, unsigned bucket_index) { struct pb_cache_entry *entry; struct pb_cache_entry *cur_entry; struct list_head *cur, *next; int64_t now; int ret = 0; struct list_head *cache = &mgr->buckets[bucket_index]; pipe_mutex_lock(mgr->mutex); entry = NULL; cur = cache->next; next = cur->next; /* search in the expired buffers, freeing them in the process */ now = os_time_get(); while (cur != cache) { cur_entry = LIST_ENTRY(struct pb_cache_entry, cur, head); if (!entry && (ret = pb_cache_is_buffer_compat(cur_entry, size, alignment, usage) > 0)) entry = cur_entry; else if (os_time_timeout(cur_entry->start, cur_entry->end, now)) destroy_buffer_locked(cur_entry); else /* This buffer (and all hereafter) are still hot in cache */ break; /* the buffer is busy (and probably all remaining ones too) */ if (ret == -1) break; cur = next; next = cur->next; } /* keep searching in the hot buffers */ if (!entry && ret != -1) { while (cur != cache) { cur_entry = LIST_ENTRY(struct pb_cache_entry, cur, head); ret = pb_cache_is_buffer_compat(cur_entry, size, alignment, usage); if (ret > 0) { entry = cur_entry; break; } if (ret == -1) break; /* no need to check the timeout here */ cur = next; next = cur->next; } } /* found a compatible buffer, return it */ if (entry) { struct pb_buffer *buf = entry->buffer; mgr->cache_size -= buf->size; LIST_DEL(&entry->head); --mgr->num_buffers; pipe_mutex_unlock(mgr->mutex); /* Increase refcount */ pipe_reference_init(&buf->reference, 1); return buf; } pipe_mutex_unlock(mgr->mutex); return NULL; }