diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..bf329fa --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +always-auth = true +registry = https://verdaccio-new.prod-tools.qualia.io/ diff --git a/binding.gyp b/binding.gyp index 2289325..824ce58 100644 --- a/binding.gyp +++ b/binding.gyp @@ -41,15 +41,7 @@ ['OS == "linux"', { 'cflags_c': [ '-std=gnu11' ], - 'variables': { - 'USE_MUSL': '&1 | head -n1 | grep "musl" | wc -l)', - }, - 'conditions': [ - ['<(USE_MUSL) == 1', - {'defines': ['CORO_ASM', '__MUSL__']}, - {'defines': ['CORO_UCONTEXT']} - ], - ], + 'defines': ['CORO_PTHREAD'], }, ], ['OS == "solaris" or OS == "sunos" or OS == "freebsd" or OS == "aix"', {'defines': ['CORO_UCONTEXT']}], @@ -65,8 +57,8 @@ ['target_arch == "arm64"', { # There's been problems getting real fibers working on arm - 'defines': ['CORO_UCONTEXT', '_XOPEN_SOURCE'], - 'defines!': ['CORO_PTHREAD', 'CORO_SJLJ', 'CORO_ASM'], + 'defines': ['CORO_PTHREAD'], + 'defines!': ['CORO_UCONTEXT', 'CORO_SJLJ', 'CORO_ASM'], }, ], ], diff --git a/fibers.js b/fibers.js index 3229dad..d79b849 100644 --- a/fibers.js +++ b/fibers.js @@ -1,132 +1 @@ -if (process.fiberLib) { - module.exports = process.fiberLib; -} else { - var fs = require('fs'), path = require('path'), detectLibc = require('detect-libc'); - - // Seed random numbers [gh-82] - Math.random(); - - // Look for binary for this platform - var modPath = path.join(__dirname, 'bin', process.platform+ '-'+ process.arch+ '-'+ process.versions.modules+ - ((process.platform === 'linux') ? '-'+ detectLibc.family : ''), 'fibers'); - try { - // Pull in fibers implementation - process.fiberLib = module.exports = require(modPath).Fiber; - } catch (ex) { - // No binary! - console.error( - '## There is an issue with `node-fibers` ##\n'+ - '`'+ modPath+ '.node` is missing.\n\n'+ - 'Try running this to fix the issue: '+ process.execPath+ ' '+ __dirname.replace(' ', '\\ ')+ '/build' - ); - console.error(ex.stack || ex.message || ex); - throw new Error('Missing binary. See message above.'); - } - - setupAsyncHacks(module.exports); -} - -function setupAsyncHacks(Fiber) { - // Older (or newer?) versions of node may not support this API - try { - var aw = process.binding('async_wrap'); - var getAsyncIdStackSize; - - if (aw.asyncIdStackSize instanceof Function) { - getAsyncIdStackSize = aw.asyncIdStackSize; - } else if (aw.constants.kStackLength !== undefined) { - getAsyncIdStackSize = function(kStackLength) { - return function() { - return aw.async_hook_fields[kStackLength]; - }; - }(aw.constants.kStackLength); - } else { - throw new Error('Couldn\'t figure out how to get async stack size'); - } - - var popAsyncContext = aw.popAsyncContext || aw.popAsyncIds; - var pushAsyncContext = aw.pushAsyncContext || aw.pushAsyncIds; - if (!popAsyncContext || !pushAsyncContext) { - throw new Error('Push/pop do not exist'); - } - - var kExecutionAsyncId; - if (aw.constants.kExecutionAsyncId === undefined) { - kExecutionAsyncId = aw.constants.kCurrentAsyncId; - } else { - kExecutionAsyncId = aw.constants.kExecutionAsyncId; - } - var kTriggerAsyncId; - if (aw.constants.kTriggerAsyncId === undefined) { - kTriggerAsyncId = aw.constants.kCurrentTriggerId; - } else { - kTriggerAsyncId = aw.constants.kTriggerAsyncId; - } - - var asyncIds = aw.async_id_fields || aw.async_uid_fields; - - function getAndClearStack() { - var ii = getAsyncIdStackSize(); - var stack = new Array(ii); - for (; ii > 0; --ii) { - var asyncId = asyncIds[kExecutionAsyncId]; - stack[ii - 1] = { - asyncId: asyncId, - triggerId: asyncIds[kTriggerAsyncId], - }; - popAsyncContext(asyncId); - } - return stack; - } - - function restoreStack(stack) { - for (var ii = 0; ii < stack.length; ++ii) { - pushAsyncContext(stack[ii].asyncId, stack[ii].triggerId); - } - } - - function logUsingFibers(fibersMethod) { - const logUseFibersLevel = +(process.env.ENABLE_LOG_USE_FIBERS || 0); - - if (!logUseFibersLevel) return; - - if (logUseFibersLevel === 1) { - console.warn(`[FIBERS_LOG] Using ${fibersMethod}.`); - return; - } - - const { LOG_USE_FIBERS_INCLUDE_IN_PATH } = process.env; - const stackFromError = new Error(`[FIBERS_LOG] Using ${fibersMethod}.`).stack; - - if ( - !LOG_USE_FIBERS_INCLUDE_IN_PATH || - stackFromError.includes(LOG_USE_FIBERS_INCLUDE_IN_PATH) - ) { - console.warn(stackFromError); - } - } - - function wrapFunction(fn, fibersMethod) { - return function () { - logUsingFibers(fibersMethod); - var stack = getAndClearStack(); - try { - return fn.apply(this, arguments); - } finally { - restoreStack(stack); - } - }; - } - - // Monkey patch methods which may long jump - Fiber.yield = wrapFunction(Fiber.yield, "Fiber.yield"); - Fiber.prototype.run = wrapFunction(Fiber.prototype.run, "Fiber.run"); - Fiber.prototype.throwInto = wrapFunction( - Fiber.prototype.throwInto, - "Fiber.throwInto" - ); - - } catch (err) { - return; - } -} +module.exports = require('./fibers_async.js'); diff --git a/fibers_async.js b/fibers_async.js new file mode 100644 index 0000000..9546a9d --- /dev/null +++ b/fibers_async.js @@ -0,0 +1,40 @@ +const { AsyncResource } = require('async_hooks'); +const _Fiber = require('./fibers_sync.js'); +if (_Fiber.Fiber) { + // if we were to mix'n'match import/require - we could end up with multiple copies of this + // it might also relate to weirdness of importing it from inside the shell/debugger + // it almost looks like the --preserve-symlinks is being ignored in the debugger/shell + module.exports = _Fiber.Fiber; +} +const asyncResourceWeakMap = new WeakMap(); +function Fiber(fn, ...args) { + const ar = new AsyncResource('Fiber'); + const actualFn = (...args1) => ar.runInAsyncScope(() => { + Fiber.current._meteor_dynamics = undefined; + fn(...args1); + }); + const _fiber = _Fiber(actualFn, ...args); + asyncResourceWeakMap.set(_fiber, ar); + return _fiber; +}; + +Fiber.__proto__ = _Fiber; +Fiber.prototype = _Fiber.prototype; + +Object.defineProperty(Fiber, 'current', { + get() { + return _Fiber.current; + } +}) + + +_Fiber.prototype.runInAsyncScope = function runInAsyncScope(fn) { + return asyncResourceWeakMap.get(this).runInAsyncScope(fn); +}; + +_Fiber[Symbol.hasInstance] = function(obj) { + // hacky + return obj instanceof Fiber || obj.run; +}; + +module.exports = _Fiber.Fiber = Fiber; diff --git a/fibers_sync.js b/fibers_sync.js new file mode 100644 index 0000000..c8d76b2 --- /dev/null +++ b/fibers_sync.js @@ -0,0 +1,135 @@ +if (process.fiberLib) { + module.exports = process.fiberLib; +} else { + var fs = require('fs'), path = require('path'), detectLibc = require('detect-libc'); + + // Seed random numbers [gh-82] + Math.random(); + + // Look for binary for this platform + var modPath = path.join(__dirname, 'bin', process.platform+ '-'+ process.arch+ '-'+ process.versions.modules+ + ((process.platform === 'linux') ? '-'+ detectLibc.family : ''), 'fibers'); + try { + // Pull in fibers implementation + process.fiberLib = module.exports = require(modPath).Fiber; + } catch (ex) { + // No binary! + console.error( + '## There is an issue with `node-fibers` ##\n'+ + '`'+ modPath+ '.node` is missing.\n\n'+ + 'Try running this to fix the issue: '+ process.execPath+ ' '+ __dirname.replace(' ', '\\ ')+ '/build' + ); + console.error(ex.stack || ex.message || ex); + throw new Error('Missing binary. See message above.'); + } + + setupAsyncHacks(module.exports); +} + +function setupAsyncHacks(Fiber) { + // Older (or newer?) versions of node may not support this API + try { + var aw = process.binding('async_wrap'); + var ah = require('async_hooks'); + var getAsyncIdStackSize; + + if (aw.asyncIdStackSize instanceof Function) { + getAsyncIdStackSize = aw.asyncIdStackSize; + } else if (aw.constants.kStackLength !== undefined) { + getAsyncIdStackSize = function(kStackLength) { + return function() { + return aw.async_hook_fields[kStackLength]; + }; + }(aw.constants.kStackLength); + } else { + throw new Error('Couldn\'t figure out how to get async stack size'); + } + + var popAsyncContext = aw.popAsyncContext || aw.popAsyncIds; + var pushAsyncContext = aw.pushAsyncContext || aw.pushAsyncIds; + if (!popAsyncContext || !pushAsyncContext) { + throw new Error('Push/pop do not exist'); + } + + var kExecutionAsyncId; + if (aw.constants.kExecutionAsyncId === undefined) { + kExecutionAsyncId = aw.constants.kCurrentAsyncId; + } else { + kExecutionAsyncId = aw.constants.kExecutionAsyncId; + } + var kTriggerAsyncId; + if (aw.constants.kTriggerAsyncId === undefined) { + kTriggerAsyncId = aw.constants.kCurrentTriggerId; + } else { + kTriggerAsyncId = aw.constants.kTriggerAsyncId; + } + + var asyncIds = aw.async_id_fields || aw.async_uid_fields; + + function getAndClearStack() { + var ii = getAsyncIdStackSize(); + var stack = new Array(ii); + for (; ii > 0; --ii) { + var asyncId = asyncIds[kExecutionAsyncId]; + stack[ii - 1] = { + asyncResource: ah.executionAsyncResource(), + asyncId: asyncId, + triggerId: asyncIds[kTriggerAsyncId], + }; + popAsyncContext(asyncId); + } + return stack; + } + + function restoreStack(stack) { + for (var ii = 0; ii < stack.length; ++ii) { + pushAsyncContext(stack[ii].asyncId, stack[ii].triggerId); + aw.execution_async_resources.push(stack[ii].asyncResource); + } + } + + const logUseFibersLevel = +(process.env.ENABLE_LOG_USE_FIBERS || 0); + const { LOG_USE_FIBERS_INCLUDE_IN_PATH } = process.env; + function logUsingFibers(fibersMethod) { + + if (!logUseFibersLevel) return; + + if (logUseFibersLevel === 1) { + console.warn(`[FIBERS_LOG] Using ${fibersMethod}.`); + return; + } + + const stackFromError = new Error(`[FIBERS_LOG] Using ${fibersMethod}.`).stack; + + if ( + !LOG_USE_FIBERS_INCLUDE_IN_PATH || + stackFromError.includes(LOG_USE_FIBERS_INCLUDE_IN_PATH) + ) { + console.warn(stackFromError); + } + } + + function wrapFunction(fn, fibersMethod) { + return function () { + logUsingFibers(fibersMethod); + var stack = getAndClearStack(); + try { + return fn.apply(this, arguments); + } finally { + restoreStack(stack); + } + }; + } + + // Monkey patch methods which may long jump + Fiber.yield = wrapFunction(Fiber.yield, "Fiber.yield"); + Fiber.prototype.run = wrapFunction(Fiber.prototype.run, "Fiber.run"); + Fiber.prototype.throwInto = wrapFunction( + Fiber.prototype.throwInto, + "Fiber.throwInto" + ); + + } catch (err) { + return; + } +} diff --git a/future.js b/future.js index 48cf7dd..8d907f9 100644 --- a/future.js +++ b/future.js @@ -156,7 +156,7 @@ Future.wait = function wait(/* ... */) { // Resumes current fiber var fiber = Fiber.current; if (!fiber) { - throw new Error('Can\'t wait without a fiber'); + throw new Error('Can\'t wait without a fiber. Most likely you called `Promise.await()` after calling `await somePromiseAPI()`. Read https://engdocs.qualia.io/qualia/advanced-topics/fibers'); } // Resolve all futures diff --git a/package-lock.json b/package-lock.json index 398b123..3e9ac2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "fibers", - "version": "5.0.0", + "version": "5.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f0d749c..b286705 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fibers", - "version": "5.0.2", + "version": "5.0.5", "description": "Cooperative multi-tasking for Javascript", "keywords": [ "fiber", @@ -15,7 +15,7 @@ ], "homepage": "https://github.com/laverdet/node-fibers", "author": "Marcel Laverdet (https://github.com/laverdet/)", - "main": "fibers", + "main": "fibers_async", "scripts": { "install": "node build.js || nodejs build.js", "test": "node test.js || nodejs test.js" diff --git a/src/coroutine.cc b/src/coroutine.cc index 027744f..e89abdc 100644 --- a/src/coroutine.cc +++ b/src/coroutine.cc @@ -148,7 +148,7 @@ void Coroutine::init(v8::Isolate* isolate) { isolate_key = v8::internal::Isolate::isolate_key_; thread_data_key = v8::internal::Isolate::per_isolate_thread_data_key_; thread_id_key = v8::internal::Isolate::thread_id_key_; -#else +#elif !defined(CORO_PTHREAD) pthread_t thread; pthread_create(&thread, NULL, find_thread_id_key, isolate); pthread_join(thread, NULL); diff --git a/src/fibers.cc b/src/fibers.cc index e26b216..68311a1 100644 --- a/src/fibers.cc +++ b/src/fibers.cc @@ -14,7 +14,18 @@ using namespace v8; // Handle legacy V8 API namespace uni { -#if V8_AT_LEAST(5, 3) +#if V8_AT_LEAST(10, 4) + // I have no idea if 10.4 is correct, I only know that from version 20 of node, this code needs to be triggered, and doing this makes it so + template + void WeakCallbackShim(const WeakCallbackInfo

& data) { + F(data.GetParameter()); + } + + template + void MakeWeak(Isolate* isolate, Persistent& handle, P* val) { + handle.SetWeak(val, WeakCallbackShim, WeakCallbackType::kParameter); + } +#elif V8_AT_LEAST(5, 3) // Actually 5.2.244 // ..or maybe actually 5.2.49 template