/** * Portions Copyright (c) Microsoft Corporation. All rights reserved. * * 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 * * THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS * OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION * ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR * PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. * * See the Apache Version 2.0 License for specific language governing * permissions and limitations under the License. */ #include "edge_common.h" void continueOnV8Thread(uv_async_t* handle, int status) { // This executes on V8 thread DBG("continueOnV8Thread"); NanScope(); uv_edge_async_t* uv_edge_async = (uv_edge_async_t*)handle; uv_async_edge_cb action = uv_edge_async->action; void* data = uv_edge_async->data; V8SynchronizationContext::CancelAction(uv_edge_async); action(data); } unsigned long V8SynchronizationContext::v8ThreadId; uv_sem_t* V8SynchronizationContext::funcWaitHandle; uv_edge_async_t* V8SynchronizationContext::uv_edge_async; void V8SynchronizationContext::Initialize() { // This executes on V8 thread DBG("V8SynchronizationContext::Initialize"); V8SynchronizationContext::uv_edge_async = new uv_edge_async_t; uv_edge_async->singleton = TRUE; uv_async_init(uv_default_loop(), &V8SynchronizationContext::uv_edge_async->uv_async, (uv_async_cb)continueOnV8Thread); V8SynchronizationContext::Unref(V8SynchronizationContext::uv_edge_async); V8SynchronizationContext::funcWaitHandle = new uv_sem_t; uv_sem_init(V8SynchronizationContext::funcWaitHandle, 1); V8SynchronizationContext::v8ThreadId = V8SynchronizationContext::GetCurrentThreadId(); } void V8SynchronizationContext::Unref(uv_edge_async_t* uv_edge_async) { DBG("V8SynchronizationContext::Unref"); uv_unref((uv_handle_t*)&uv_edge_async->uv_async); } uv_edge_async_t* V8SynchronizationContext::RegisterAction(uv_async_edge_cb action, void* data) { DBG("V8SynchronizationContext::RegisterAction"); if (V8SynchronizationContext::GetCurrentThreadId() == V8SynchronizationContext::v8ThreadId) { // This executes on V8 thread. // Allocate new uv_edge_async. uv_edge_async_t* uv_edge_async = new uv_edge_async_t; uv_edge_async->action = action; uv_edge_async->data = data; uv_edge_async->singleton = FALSE; uv_async_init(uv_default_loop(), &uv_edge_async->uv_async, (uv_async_cb)continueOnV8Thread); return uv_edge_async; } else { // This executes on CLR thread. // Acquire exlusive access to uv_edge_async previously initialized on V8 thread. uv_sem_wait(V8SynchronizationContext::funcWaitHandle); V8SynchronizationContext::uv_edge_async->action = action; V8SynchronizationContext::uv_edge_async->data = data; return V8SynchronizationContext::uv_edge_async; } } void V8SynchronizationContext::ExecuteAction(uv_edge_async_t* uv_edge_async) { DBG("V8SynchronizationContext::ExecuteAction"); // Transfer control to completeOnV8hread method executing on V8 thread uv_async_send(&uv_edge_async->uv_async); } void close_uv_edge_async_cb(uv_handle_t* handle) { uv_edge_async_t* uv_edge_async = (uv_edge_async_t*)handle; delete uv_edge_async; } void V8SynchronizationContext::CancelAction(uv_edge_async_t* uv_edge_async) { DBG("V8SynchronizationContext::CancelAction"); if (uv_edge_async->singleton) { // This is a cancellation of an action registered on CLR thread. // Release the wait handle to allow the uv_edge_async reuse by another CLR thread. uv_edge_async->action = NULL; uv_edge_async->data = NULL; uv_sem_post(V8SynchronizationContext::funcWaitHandle); } else { // This is a cancellation of an action registered on V8 thread. // Unref the handle to stop preventing the process from exiting. // V8SynchronizationContext::Unref(uv_edge_async); uv_close((uv_handle_t*)&uv_edge_async->uv_async, close_uv_edge_async_cb); } } unsigned long V8SynchronizationContext::GetCurrentThreadId() { #ifdef _WIN32 return (unsigned long)::GetCurrentThreadId(); #else return (unsigned long)pthread_self(); #endif }