blob: bfb3a702356fffcc083591c83cccaf101b52264d [file]
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/shared_queue.h"
#include <deque>
#include <map>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <unordered_set>
#include "include/proxy-wasm/vm_id_handle.h"
namespace proxy_wasm {
SharedQueue &getGlobalSharedQueue() {
static auto *ptr = new SharedQueue;
return *ptr;
}
SharedQueue::SharedQueue(bool register_vm_id_callback) {
if (register_vm_id_callback) {
registerVmIdHandleCallback([this](std::string_view vm_id) { this->deleteByVmId(vm_id); });
}
}
void SharedQueue::deleteByVmId(std::string_view vm_id) {
std::lock_guard<std::mutex> lock(mutex_);
auto queue_keys = vm_queue_keys_.find(std::string(vm_id));
if (queue_keys != vm_queue_keys_.end()) {
for (const auto &queue_key : queue_keys->second) {
auto token = queue_tokens_.find(queue_key);
if (token != queue_tokens_.end()) {
queues_.erase(token->second);
queue_tokens_.erase(token);
}
}
vm_queue_keys_.erase(queue_keys);
}
}
uint32_t SharedQueue::nextQueueToken() {
// TODO(@mathetake): Should we handle the case where the queue overflows, i.e. the number of used
// tokens exceeds the max of uint32? If it overflows, the following loop never exits.
while (true) {
uint32_t token = next_queue_token_++;
if (token == 0) {
continue; // 0 is an illegal token.
}
if (queues_.find(token) == queues_.end()) {
return token;
}
}
}
uint32_t SharedQueue::registerQueue(std::string_view vm_id, std::string_view queue_name,
uint32_t context_id, CallOnThreadFunction call_on_thread,
std::string_view vm_key) {
std::lock_guard<std::mutex> lock(mutex_);
auto key = std::make_pair(std::string(vm_id), std::string(queue_name));
auto it = queue_tokens_.insert(std::make_pair(key, static_cast<uint32_t>(0)));
if (it.second) {
it.first->second = nextQueueToken();
auto vid = std::string(vm_id);
QueueKeySet *queue_keys;
auto map_it = vm_queue_keys_.find(vid);
if (map_it == vm_queue_keys_.end()) {
queue_keys = &vm_queue_keys_[vid];
} else {
queue_keys = &map_it->second;
}
queue_keys->insert(key);
}
uint32_t token = it.first->second;
auto &q = queues_[token];
q.vm_key = std::string(vm_key);
q.context_id = context_id;
q.call_on_thread = std::move(call_on_thread);
// Preserve any existing data.
return token;
}
uint32_t SharedQueue::resolveQueue(std::string_view vm_id, std::string_view queue_name) {
std::lock_guard<std::mutex> lock(mutex_);
auto key = std::make_pair(std::string(vm_id), std::string(queue_name));
auto it = queue_tokens_.find(key);
if (it != queue_tokens_.end()) {
return it->second;
}
return 0; // N.B. zero indicates that the queue was not found.
}
WasmResult SharedQueue::dequeue(uint32_t token, std::string *data) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = queues_.find(token);
if (it == queues_.end()) {
return WasmResult::NotFound;
}
if (it->second.queue.empty()) {
return WasmResult::Empty;
}
*data = it->second.queue.front();
it->second.queue.pop_front();
return WasmResult::Ok;
}
WasmResult SharedQueue::enqueue(uint32_t token, std::string_view value) {
std::string vm_key;
uint32_t context_id;
CallOnThreadFunction call_on_thread;
{
std::lock_guard<std::mutex> lock(mutex_);
auto it = queues_.find(token);
if (it == queues_.end()) {
return WasmResult::NotFound;
}
Queue *target_queue = &(it->second);
vm_key = target_queue->vm_key;
context_id = target_queue->context_id;
call_on_thread = target_queue->call_on_thread;
target_queue->queue.emplace_back(value);
}
call_on_thread([vm_key, context_id, token] {
// This code may or may not execute in another thread.
// Make sure that the lock is no longer held here.
auto wasm = getThreadLocalWasm(vm_key);
if (wasm) {
auto *context = wasm->wasm()->getContext(context_id);
if (context != nullptr) {
context->onQueueReady(token);
}
}
});
return WasmResult::Ok;
}
} // namespace proxy_wasm